Skip to content

Commit 71e22fa

Browse files
committed
migrate 200 IT file
1 parent 45da3e4 commit 71e22fa

File tree

2 files changed

+288
-0
lines changed

2 files changed

+288
-0
lines changed

test/200_fullchain-bot.test.ts

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
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 { expect } from 'hardhat';
7+
import { loadHardhatFixtureDeployment } from '../scripts/hardhat-fixture-deployer';
8+
import { IexecInterfaceNative, IexecInterfaceNative__factory } from '../typechain';
9+
import { OrdersActors, OrdersAssets, OrdersPrices, buildOrders } from '../utils/createOrders';
10+
import {
11+
PocoMode,
12+
TaskStatusEnum,
13+
buildUtf8ResultAndDigest,
14+
getIexecAccounts,
15+
} from '../utils/poco-tools';
16+
import { IexecWrapper } from './utils/IexecWrapper';
17+
18+
const standardDealTag = '0x0000000000000000000000000000000000000000000000000000000000000000';
19+
const appPrice = 1000;
20+
const datasetPrice = 1_000_000;
21+
const workerpoolPrice = 1_000_000_000;
22+
23+
let proxyAddress: string;
24+
let iexecPoco: IexecInterfaceNative;
25+
let iexecWrapper: IexecWrapper;
26+
let [appAddress, workerpoolAddress, datasetAddress]: string[] = [];
27+
let [
28+
requester,
29+
appProvider,
30+
datasetProvider,
31+
scheduler,
32+
anyone,
33+
worker1,
34+
worker2,
35+
worker3,
36+
worker4,
37+
worker5,
38+
]: SignerWithAddress[] = [];
39+
let ordersActors: OrdersActors;
40+
let ordersAssets: OrdersAssets;
41+
let ordersPrices: OrdersPrices;
42+
43+
describe('Integration tests', function () {
44+
beforeEach('Deploy', async () => {
45+
// Deploy all contracts
46+
proxyAddress = await loadHardhatFixtureDeployment();
47+
// Initialize test environment
48+
await loadFixture(initFixture);
49+
});
50+
51+
async function initFixture() {
52+
const accounts = await getIexecAccounts();
53+
({
54+
requester,
55+
appProvider,
56+
datasetProvider,
57+
scheduler,
58+
anyone,
59+
worker1,
60+
worker2,
61+
worker3,
62+
worker4,
63+
worker5,
64+
} = accounts);
65+
iexecWrapper = new IexecWrapper(proxyAddress, accounts);
66+
({ appAddress, datasetAddress, workerpoolAddress } = await iexecWrapper.createAssets());
67+
iexecPoco = IexecInterfaceNative__factory.connect(proxyAddress, anyone);
68+
ordersActors = {
69+
appOwner: appProvider,
70+
datasetOwner: datasetProvider,
71+
workerpoolOwner: scheduler,
72+
requester: requester,
73+
};
74+
ordersAssets = {
75+
app: appAddress,
76+
dataset: datasetAddress,
77+
workerpool: workerpoolAddress,
78+
};
79+
ordersPrices = {
80+
app: appPrice,
81+
dataset: datasetPrice,
82+
workerpool: workerpoolPrice,
83+
};
84+
}
85+
86+
it('Task Lifecycle with BoT Replication and Error Handling', async function () {
87+
const workers = [worker1, worker2, worker3, worker4, worker5];
88+
// Create deal.
89+
const volume = 3;
90+
const tasksAndWorkers: {
91+
[key: number]: {
92+
worker: SignerWithAddress;
93+
useEnclave: boolean;
94+
result: string;
95+
}[];
96+
} = {
97+
0: [
98+
{ worker: worker1, useEnclave: false, result: 'iExec BOT 0' },
99+
{ worker: worker2, useEnclave: false, result: 'iExec BOT 0' },
100+
],
101+
1: [
102+
{ worker: worker2, useEnclave: true, result: 'iExec BOT 1' },
103+
{ worker: worker3, useEnclave: true, result: 'iExec BOT 1' },
104+
],
105+
2: [
106+
{ worker: worker1, useEnclave: false, result: 'iExec BOT 2' },
107+
{ worker: worker3, useEnclave: false, result: '<timeout reached>' },
108+
{ worker: worker2, useEnclave: true, result: 'iExec BOT 2' },
109+
{ worker: worker4, useEnclave: true, result: 'iExec BOT 2' },
110+
{ worker: worker5, useEnclave: true, result: 'iExec BOT 2' },
111+
],
112+
};
113+
const orders = buildOrders({
114+
assets: ordersAssets,
115+
prices: ordersPrices,
116+
requester: requester.address,
117+
tag: standardDealTag,
118+
volume,
119+
trust: 4,
120+
});
121+
const { dealId, dealPrice, schedulerStakePerDeal } = await iexecWrapper.signAndMatchOrders(
122+
...orders.toArray(),
123+
);
124+
const taskPrice = appPrice + datasetPrice + workerpoolPrice;
125+
const schedulerStakePerTask = schedulerStakePerDeal / volume;
126+
const workerRewardPerTask = await iexecWrapper.computeWorkerRewardPerTask(
127+
dealId,
128+
PocoMode.CLASSIC,
129+
);
130+
const schedulerRewardPerTask = workerpoolPrice - workerRewardPerTask;
131+
// Save frozens
132+
133+
const accounts = [requester, scheduler, appProvider, datasetProvider, ...workers];
134+
const accountsInitialFrozens = await getInitialFrozens(accounts);
135+
// Track initial scores
136+
// Finalize each task and check balance changes.
137+
const workerStakePerTask = await iexecPoco
138+
.viewDeal(dealId)
139+
.then((deal) => deal.workerStake.toNumber());
140+
for (let taskIndex = 0; taskIndex < volume; taskIndex++) {
141+
const taskId = await iexecWrapper.initializeTask(dealId, taskIndex);
142+
const initialScores = await getInitialScores(workers);
143+
const contributions = tasksAndWorkers[taskIndex];
144+
for (const contribution of contributions) {
145+
const { worker, useEnclave, result } = contribution;
146+
const { resultDigest } = buildUtf8ResultAndDigest(result);
147+
148+
if (useEnclave) {
149+
await iexecWrapper.contributeToTeeTask(dealId, taskIndex, resultDigest, worker);
150+
} else {
151+
await iexecWrapper.contributeToTask(dealId, taskIndex, resultDigest, worker);
152+
}
153+
}
154+
// Reveal contributions for all workers
155+
for (const contribution of contributions) {
156+
const { worker, result } = contribution;
157+
const { resultDigest } = buildUtf8ResultAndDigest(result);
158+
if (result !== '<timeout reached>') {
159+
await iexecPoco
160+
.connect(worker)
161+
.reveal(taskId, resultDigest)
162+
.then((tx) => tx.wait());
163+
}
164+
}
165+
const { results } = buildUtf8ResultAndDigest(tasksAndWorkers[taskIndex][0].result);
166+
const finalizeTx = await iexecPoco.connect(scheduler).finalize(taskId, results, '0x');
167+
await finalizeTx.wait();
168+
expect((await iexecPoco.viewTask(taskId)).status).to.equal(TaskStatusEnum.COMPLETED);
169+
// Multiply amount by the number of finalized tasks to correctly compute
170+
// stake and reward amounts.
171+
const completedTasks = taskIndex + 1;
172+
// Verify token balance changes
173+
const expectedProxyBalanceChange = -(
174+
taskPrice * completedTasks +
175+
schedulerStakePerTask * completedTasks
176+
);
177+
const winningWorkers = contributions
178+
.filter(
179+
(contribution) => contribution.result === 'iExec BOT ' + taskIndex.toString(),
180+
)
181+
.map((contribution) => contribution.worker);
182+
const loosingWorkers = contributions
183+
.filter(
184+
(contribution) => contribution.result !== 'iExec BOT ' + taskIndex.toString(),
185+
)
186+
.map((contribution) => contribution.worker);
187+
const nonParticipantWorkers = workers.filter(
188+
(worker) => !winningWorkers.includes(worker) && !loosingWorkers.includes(worker),
189+
);
190+
const expectedWorkerBalanceChange =
191+
workerStakePerTask + workerRewardPerTask / winningWorkers.length;
192+
expect(finalizeTx).to.changeTokenBalances(
193+
iexecPoco,
194+
[proxyAddress, ...accounts],
195+
[
196+
expectedProxyBalanceChange, // Proxy
197+
-taskPrice * completedTasks, // Requester
198+
schedulerStakePerTask + schedulerRewardPerTask, // Scheduler
199+
appPrice, // AppProvider
200+
datasetPrice, // DatasetProvider
201+
...winningWorkers.map(() => expectedWorkerBalanceChange), // winning workers
202+
...loosingWorkers.map(() => 0), // loosing workers
203+
...nonParticipantWorkers.map(() => 0), // non participant workers
204+
],
205+
);
206+
// Calculate expected frozen changes
207+
const expectedFrozenChanges = [
208+
0, // Proxy
209+
-taskPrice * completedTasks, // Requester
210+
-schedulerStakePerTask * completedTasks, // Scheduler
211+
0, // AppProvider
212+
0, // DatasetProvider
213+
...workers.map(() => 0), // Add 0 for each worker
214+
];
215+
await checkFrozenChanges(accountsInitialFrozens, expectedFrozenChanges);
216+
await validateScores(
217+
initialScores,
218+
winningWorkers,
219+
loosingWorkers,
220+
nonParticipantWorkers,
221+
);
222+
}
223+
});
224+
async function getInitialFrozens(accounts: SignerWithAddress[]) {
225+
let initialFrozens = [
226+
{
227+
address: proxyAddress,
228+
frozen: (await iexecPoco.frozenOf(proxyAddress)).toNumber(),
229+
},
230+
];
231+
for (const account of accounts) {
232+
initialFrozens.push({
233+
address: account.address,
234+
frozen: (await iexecPoco.frozenOf(account.address)).toNumber(),
235+
});
236+
}
237+
return initialFrozens;
238+
}
239+
240+
async function getInitialScores(
241+
workers: SignerWithAddress[],
242+
): Promise<{ [address: string]: number }> {
243+
const scores: { [address: string]: number } = {};
244+
for (const worker of workers) {
245+
scores[worker.address] = (await iexecPoco.viewScore(worker.address)).toNumber();
246+
}
247+
return scores;
248+
}
249+
});
250+
251+
async function checkFrozenChanges(
252+
accountsInitialFrozens: { address: string; frozen: number }[],
253+
expectedFrozenChanges: number[],
254+
) {
255+
for (let i = 0; i < accountsInitialFrozens.length; i++) {
256+
const message = `Failed with account at index ${i}`;
257+
expect(await iexecPoco.frozenOf(accountsInitialFrozens[i].address)).to.equal(
258+
accountsInitialFrozens[i].frozen + expectedFrozenChanges[i],
259+
message,
260+
);
261+
}
262+
}
263+
264+
async function validateScores(
265+
initialScores: { [address: string]: number },
266+
winningWorkers: SignerWithAddress[],
267+
loosingWorkers: SignerWithAddress[],
268+
nonParticipantWorkers: SignerWithAddress[],
269+
) {
270+
for (const winningWorker of winningWorkers) {
271+
const currentScore = (await iexecPoco.viewScore(winningWorker.address)).toNumber();
272+
expect(currentScore, `Worker ${winningWorker.address} score mismatch`).to.equal(
273+
initialScores[winningWorker.address] + 1,
274+
);
275+
}
276+
for (const loosingWorker of loosingWorkers) {
277+
const currentScore = (await iexecPoco.viewScore(loosingWorker.address)).toNumber();
278+
expect(currentScore, `Worker ${loosingWorker.address} score mismatch`).to.equal(
279+
initialScores[loosingWorker.address] - 1,
280+
);
281+
}
282+
for (const nonParticipantWorker of nonParticipantWorkers) {
283+
const currentScore = (await iexecPoco.viewScore(nonParticipantWorker.address)).toNumber();
284+
expect(currentScore, `Worker ${nonParticipantWorker.address} score mismatch`).to.equal(
285+
initialScores[nonParticipantWorker.address],
286+
);
287+
}
288+
}

0 commit comments

Comments
 (0)