Skip to content

Commit 56a05dc

Browse files
authored
Migrate 201_fullchain-bot-dualPool.js file to Hardhat/TS (#171)
2 parents 42c61c4 + b2cb3a3 commit 56a05dc

File tree

6 files changed

+286
-21
lines changed

6 files changed

+286
-21
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- 000_fullchain-5workers-1error.js (#160, #162)
1616
- Clean ToDo (#163)
1717
- 200_fullchain-bot.js (#164, #166)
18+
- 201_fullchain-bot-dualPool.js (#171)
1819
- Fix balance checks in integration tests (#165)
1920
- Remove `smock` from unit tests:
2021
- IexecEscrow.v8 (#154, #155)

test/000_fullchain.test.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,9 @@ describe('Integration tests', function () {
118118
volume,
119119
trust: workers.length ** 2 - 1,
120120
});
121-
const { dealId, dealPrice, schedulerStakePerDeal } =
122-
await iexecWrapper.signAndSponsorMatchOrders(...orders.toArray());
121+
const { dealId, schedulerStakePerDeal } = await iexecWrapper.signAndSponsorMatchOrders(
122+
...orders.toArray(),
123+
);
123124
const taskPrice = appPrice + datasetPrice + workerpoolPrice;
124125
const schedulerStakePerTask = schedulerStakePerDeal / volume;
125126
const workersRewardPerTask = await iexecWrapper.computeWorkersRewardPerTask(
@@ -209,7 +210,7 @@ describe('Integration tests', function () {
209210
volume,
210211
trust: workers.length ** 2 - 1,
211212
});
212-
const { dealId, dealPrice, schedulerStakePerDeal } = await iexecWrapper.signAndMatchOrders(
213+
const { dealId, schedulerStakePerDeal } = await iexecWrapper.signAndMatchOrders(
213214
...orders.toArray(),
214215
);
215216
const taskPrice = appPrice + datasetPrice + workerpoolPrice;
@@ -298,8 +299,9 @@ describe('Integration tests', function () {
298299
volume,
299300
trust: 1,
300301
});
301-
const { dealId, dealPrice, schedulerStakePerDeal } =
302-
await iexecWrapper.signAndSponsorMatchOrders(...orders.toArray());
302+
const { dealId, schedulerStakePerDeal } = await iexecWrapper.signAndSponsorMatchOrders(
303+
...orders.toArray(),
304+
);
303305
const taskPrice = appPrice + datasetPrice + workerpoolPrice;
304306
const schedulerStakePerTask = schedulerStakePerDeal / volume;
305307
const workersRewardPerTask = await iexecWrapper.computeWorkersRewardPerTask(
@@ -376,7 +378,7 @@ describe('Integration tests', function () {
376378
volume,
377379
trust: 1,
378380
});
379-
const { dealId, dealPrice, schedulerStakePerDeal } = await iexecWrapper.signAndMatchOrders(
381+
const { dealId, schedulerStakePerDeal } = await iexecWrapper.signAndMatchOrders(
380382
...orders.toArray(),
381383
);
382384
const taskPrice = appPrice + datasetPrice + workerpoolPrice;
@@ -601,7 +603,8 @@ describe('Integration tests', function () {
601603
});
602604
}
603605
});
604-
it(`[7] No sponsorship, no beneficiary, no callback, no BoT, up to 5 workers with 1 bad worker`, async function () {
606+
607+
it('[7] No sponsorship, no beneficiary, no callback, no BoT, up to 5 workers with 1 bad worker', async function () {
605608
const volume = 1;
606609
const allWorkers = [worker1, worker2, worker3, worker4, worker5];
607610
const { resultDigest: badResultDigest } = buildUtf8ResultAndDigest('bad-result');
Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
// SPDX-FileCopyrightText: 2024 IEXEC BLOCKCHAIN TECH <[email protected]>
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
5+
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
6+
import { ethers, expect } from 'hardhat';
7+
import { loadHardhatFixtureDeployment } from '../scripts/hardhat-fixture-deployer';
8+
import {
9+
IexecInterfaceNative,
10+
IexecInterfaceNative__factory,
11+
IexecPocoAccessors,
12+
IexecPocoAccessors__factory,
13+
} from '../typechain';
14+
import {
15+
IexecOrders,
16+
OrdersActors,
17+
OrdersAssets,
18+
OrdersPrices,
19+
buildOrders,
20+
} from '../utils/createOrders';
21+
import {
22+
PocoMode,
23+
TaskStatusEnum,
24+
buildUtf8ResultAndDigest,
25+
getIexecAccounts,
26+
} from '../utils/poco-tools';
27+
import { IexecWrapper } from './utils/IexecWrapper';
28+
29+
const standardDealTag = '0x0000000000000000000000000000000000000000000000000000000000000000';
30+
const appPrice = 1000;
31+
const datasetPrice = 1_000_000;
32+
const workerpoolPrice1 = 1_000_000_015;
33+
const workerpoolPrice2 = 1_000_000_025;
34+
const { results, resultDigest } = buildUtf8ResultAndDigest('result');
35+
36+
let proxyAddress: string;
37+
let iexecPoco: IexecInterfaceNative;
38+
let iexecPocoAccessors: IexecPocoAccessors; // To use `computeDealVolume()`
39+
let iexecWrapper: IexecWrapper;
40+
let [appAddress, workerpoolAddress, datasetAddress]: string[] = [];
41+
let [requester, appProvider, datasetProvider, scheduler, anyone, worker1]: SignerWithAddress[] = [];
42+
let ordersActors: OrdersActors;
43+
let ordersAssets: OrdersAssets;
44+
let ordersPrices: OrdersPrices;
45+
46+
describe('Integration tests', function () {
47+
beforeEach('Deploy', async () => {
48+
// Deploy all contracts
49+
proxyAddress = await loadHardhatFixtureDeployment();
50+
// Initialize test environment
51+
await loadFixture(initFixture);
52+
});
53+
54+
async function initFixture() {
55+
const accounts = await getIexecAccounts();
56+
({ requester, appProvider, datasetProvider, scheduler, anyone, worker1 } = accounts);
57+
iexecWrapper = new IexecWrapper(proxyAddress, accounts);
58+
({ appAddress, datasetAddress, workerpoolAddress } = await iexecWrapper.createAssets());
59+
iexecPoco = IexecInterfaceNative__factory.connect(proxyAddress, anyone);
60+
iexecPocoAccessors = IexecPocoAccessors__factory.connect(proxyAddress, ethers.provider);
61+
ordersActors = {
62+
appOwner: appProvider,
63+
datasetOwner: datasetProvider,
64+
workerpoolOwner: scheduler,
65+
requester: requester,
66+
};
67+
ordersAssets = {
68+
app: appAddress,
69+
dataset: datasetAddress,
70+
workerpool: workerpoolAddress,
71+
};
72+
ordersPrices = {
73+
app: appPrice,
74+
dataset: datasetPrice,
75+
workerpool: 0, // Overridden below.
76+
};
77+
}
78+
79+
/**
80+
* A test to run full workflow (matchOrders..finalize) with 2 orders having 2 different volumes
81+
* for the same workerpool and only 1 request order.
82+
*/
83+
it('[1] No sponsorship, no beneficiary, no callback, BoT, no replication, 2 workerpool orders', async function () {
84+
const volume = 3;
85+
const workerpoolOrderVolume1 = 2;
86+
const workerpoolOrderVolume2 = 10;
87+
const dealVolume1 = Math.min(workerpoolOrderVolume1, volume); // min(2, 3);
88+
const dealVolume2 = Math.min(workerpoolOrderVolume2, volume - dealVolume1); // min(10, 1)
89+
const taskPrice1 = appPrice + datasetPrice + workerpoolPrice1;
90+
const taskPrice2 = appPrice + datasetPrice + workerpoolPrice2;
91+
// Create default orders.
92+
const {
93+
appOrder,
94+
datasetOrder,
95+
workerpoolOrder,
96+
requesterOrder: requestOrder,
97+
} = buildOrders({
98+
assets: ordersAssets,
99+
prices: ordersPrices,
100+
requester: requester.address,
101+
tag: standardDealTag,
102+
volume,
103+
}).toObject();
104+
// Create 2 different orders for the same workerpool.
105+
const workerpoolOrder1 = { ...workerpoolOrder }; // Shallow cloning is fine here.
106+
const workerpoolOrder2 = { ...workerpoolOrder };
107+
workerpoolOrder1.volume = workerpoolOrderVolume1;
108+
workerpoolOrder1.workerpoolprice = workerpoolPrice1;
109+
workerpoolOrder2.volume = workerpoolOrderVolume2;
110+
workerpoolOrder2.workerpoolprice = workerpoolPrice2;
111+
requestOrder.workerpoolmaxprice = Math.max(workerpoolPrice1, workerpoolPrice2);
112+
// Match both workerpool orders with the same request order.
113+
const dealOrders1 = new IexecOrders(
114+
appOrder,
115+
datasetOrder,
116+
workerpoolOrder1,
117+
requestOrder,
118+
).toArray();
119+
const dealOrders2 = new IexecOrders(
120+
appOrder,
121+
datasetOrder,
122+
workerpoolOrder2,
123+
requestOrder,
124+
).toArray();
125+
expect(await iexecPocoAccessors.computeDealVolume(...dealOrders1)).to.equal(dealVolume1);
126+
const {
127+
dealId: dealId1,
128+
taskIndex: taskIndex1,
129+
schedulerStakePerDeal: schedulerStakeForDeal1,
130+
} = await iexecWrapper.signAndMatchOrders(
131+
appOrder,
132+
datasetOrder,
133+
workerpoolOrder1,
134+
requestOrder,
135+
); // First task index is 0.
136+
expect(await iexecPocoAccessors.computeDealVolume(...dealOrders2)).to.equal(dealVolume2);
137+
const {
138+
dealId: dealId2,
139+
taskIndex: taskIndex2,
140+
schedulerStakePerDeal: schedulerStakeForDeal2,
141+
} = await iexecWrapper.signAndMatchOrders(
142+
appOrder,
143+
datasetOrder,
144+
workerpoolOrder2,
145+
requestOrder,
146+
); // First task index is 2.
147+
const deal1 = await iexecPoco.viewDeal(dealId1);
148+
expect(deal1.botFirst).to.equal(0);
149+
expect(deal1.botSize).to.equal(dealVolume1);
150+
const deal2 = await iexecPoco.viewDeal(dealId2);
151+
expect(deal2.botFirst).to.equal(dealVolume1);
152+
expect(deal2.botSize).to.equal(dealVolume2);
153+
// Compute stakes and rewards for each deal.
154+
const schedulerStakePerTaskOfDeal1 = schedulerStakeForDeal1 / dealVolume1;
155+
const schedulerStakePerTaskOfDeal2 = schedulerStakeForDeal2 / dealVolume2;
156+
const workersRewardPerTaskOfDeal1 = await iexecWrapper.computeWorkersRewardPerTask(
157+
dealId1,
158+
PocoMode.CLASSIC,
159+
);
160+
const workersRewardPerTaskOfDeal2 = await iexecWrapper.computeWorkersRewardPerTask(
161+
dealId2,
162+
PocoMode.CLASSIC,
163+
);
164+
const schedulerRewardPerTaskOfDeal1 = workerpoolPrice1 - workersRewardPerTaskOfDeal1;
165+
const schedulerRewardPerTaskOfDeal2 = workerpoolPrice2 - workersRewardPerTaskOfDeal2;
166+
// Finalize each task and run checks.
167+
await runTaskThenCheckBalancesAndVolumes(
168+
dealId1,
169+
taskIndex1,
170+
taskPrice1,
171+
schedulerStakePerTaskOfDeal1,
172+
schedulerRewardPerTaskOfDeal1,
173+
workersRewardPerTaskOfDeal1,
174+
);
175+
await runTaskThenCheckBalancesAndVolumes(
176+
dealId1,
177+
taskIndex1 + 1,
178+
taskPrice1,
179+
schedulerStakePerTaskOfDeal1,
180+
schedulerRewardPerTaskOfDeal1,
181+
workersRewardPerTaskOfDeal1,
182+
);
183+
await runTaskThenCheckBalancesAndVolumes(
184+
dealId2,
185+
taskIndex2,
186+
taskPrice2,
187+
schedulerStakePerTaskOfDeal2,
188+
schedulerRewardPerTaskOfDeal2,
189+
workersRewardPerTaskOfDeal2,
190+
);
191+
// Check remaining volumes.
192+
expect(await iexecPoco.viewConsumed(iexecWrapper.hashOrder(requestOrder))).to.equal(volume);
193+
expect(await iexecPoco.viewConsumed(iexecWrapper.hashOrder(workerpoolOrder1))).to.equal(
194+
dealVolume1,
195+
);
196+
expect(await iexecPoco.viewConsumed(iexecWrapper.hashOrder(workerpoolOrder2))).to.equal(
197+
dealVolume2,
198+
);
199+
});
200+
201+
async function runTaskThenCheckBalancesAndVolumes(
202+
dealId: string,
203+
taskIndex: number,
204+
taskPrice: number,
205+
schedulerStake: number,
206+
schedulerReward: number,
207+
workerReward: number,
208+
) {
209+
// Save frozens before task execution.
210+
const accounts = [requester, scheduler, appProvider, datasetProvider, worker1];
211+
const accountsInitialFrozens = await iexecWrapper.getInitialFrozens(accounts);
212+
// Run task.
213+
const taskId = await iexecWrapper.initializeTask(dealId, taskIndex);
214+
const { workerStakePerTask: workerStake } = await iexecWrapper.contributeToTask(
215+
dealId,
216+
taskIndex,
217+
resultDigest,
218+
worker1,
219+
);
220+
await iexecPoco
221+
.connect(worker1)
222+
.reveal(taskId, resultDigest)
223+
.then((tx) => tx.wait());
224+
const finalizeTx = await iexecPoco.connect(scheduler).finalize(taskId, results, '0x');
225+
await finalizeTx.wait();
226+
// Check task.
227+
const task = await iexecPoco.viewTask(taskId);
228+
expect(task.status).to.equal(TaskStatusEnum.COMPLETED);
229+
expect(task.idx).to.equal(taskIndex);
230+
// Verify token balance changes.
231+
const expectedProxyBalanceChange = -(taskPrice + schedulerStake + workerStake);
232+
await expect(finalizeTx).to.changeTokenBalances(
233+
iexecPoco,
234+
[proxyAddress, requester, scheduler, appProvider, datasetProvider, worker1],
235+
[
236+
expectedProxyBalanceChange, // Proxy
237+
0, // Requester
238+
schedulerStake + schedulerReward, // Scheduler
239+
appPrice, // AppProvider
240+
datasetPrice, // DatasetProvider
241+
workerStake + workerReward, // Worker
242+
],
243+
);
244+
// Calculate expected frozen changes
245+
const expectedFrozenChanges = [
246+
0, // Proxy
247+
-taskPrice, // Requester
248+
-schedulerStake, // Scheduler
249+
0, // AppProvider
250+
0, // DatasetProvider
251+
0, // Worker
252+
];
253+
await iexecWrapper.checkFrozenChanges(accountsInitialFrozens, expectedFrozenChanges);
254+
}
255+
});

test/byContract/IexecPoco/IexecPoco1.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,8 @@ describe('IexecPoco1', () => {
638638
// - identity groups
639639
// - pre-signatures
640640
// - low orders volumes
641+
// - test when the lowest volume is in one of the orders
642+
// - test when the lowest volume in order < unconsumed volume
641643
// - multiple matches of the same order
642644

643645
it('Should fail when categories are different', async () => {

test/utils/IexecWrapper.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
IexecLibOrders_v5,
1818
IexecMaintenanceDelegate__factory,
1919
IexecPoco2__factory,
20+
IexecPocoAccessors__factory,
2021
IexecPocoBoostAccessors__factory,
2122
RLC__factory,
2223
WorkerpoolRegistry,
@@ -135,7 +136,7 @@ export class IexecWrapper {
135136
this.accounts.anyone,
136137
).m_workerStakeRatioPolicy()
137138
).toNumber();
138-
return (workerpoolPrice * workerStakeRatio) / 100;
139+
return Math.floor((workerpoolPrice * workerStakeRatio) / 100);
139140
}
140141

141142
/**
@@ -153,8 +154,8 @@ export class IexecWrapper {
153154
}
154155

155156
/**
156-
* Compute the amount of RLC tokens that the worker receives
157-
* as a reward per task.
157+
* Compute the amount of RLC tokens that are rewarded to workers when
158+
* a task is finalized.
158159
* @param dealId
159160
* @param mode
160161
* @returns
@@ -173,10 +174,9 @@ export class IexecWrapper {
173174
this.proxyAddress,
174175
ethers.provider,
175176
).viewDeal(dealId);
176-
// (workerpoolPrice * workerRatio) / 100
177-
return (
178-
(deal.workerpool.price.toNumber() * (100 - deal.schedulerRewardRatio.toNumber())) / 100
179-
);
177+
// reward = (workerpoolPrice * workersRatio) / 100
178+
const workersRewardRatio = 100 - deal.schedulerRewardRatio.toNumber();
179+
return Math.floor((deal.workerpool.price.toNumber() * workersRewardRatio) / 100);
180180
}
181181

182182
async setTeeBroker(brokerAddress: string) {
@@ -256,14 +256,18 @@ export class IexecWrapper {
256256
const workerpoolOrder = orders.workerpool;
257257
const requestOrder = orders.requester;
258258
const taskIndex = (
259-
await IexecAccessors__factory.connect(
260-
this.proxyAddress,
261-
this.accounts.anyone,
262-
).viewConsumed(this.hashOrder(requestOrder))
259+
await IexecAccessors__factory.connect(this.proxyAddress, ethers.provider).viewConsumed(
260+
this.hashOrder(requestOrder),
261+
)
263262
).toNumber();
264263
const dealId = getDealId(this.domain, requestOrder, taskIndex);
265264
const taskId = getTaskId(dealId, taskIndex);
266-
const volume = Number(requestOrder.volume);
265+
const volume = (
266+
await IexecPocoAccessors__factory.connect(
267+
this.proxyAddress,
268+
ethers.provider,
269+
).computeDealVolume(appOrder, datasetOrder, workerpoolOrder, requestOrder)
270+
).toNumber();
267271
const taskPrice =
268272
Number(appOrder.appprice) +
269273
Number(datasetOrder.datasetprice) +

0 commit comments

Comments
 (0)