Skip to content

Commit 4489ff4

Browse files
authored
Migrate 300 fullchain reopen file (#170)
2 parents 424b107 + a41e8bb commit 4489ff4

File tree

3 files changed

+232
-0
lines changed

3 files changed

+232
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
- 200_fullchain-bot.js (#164, #166)
1818
- 201_fullchain-bot-dualPool.js (#171, #172)
1919
- Fix balance checks in integration tests (#165)
20+
- 300_fullchain-reopen.js (#170)
2021
- Remove `smock` from unit tests:
2122
- IexecEscrow.v8 (#154, #155)
2223
- IexecPocoDelegate (#149, #151)

test/300_fullchain-reopen.test.ts

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
// SPDX-FileCopyrightText: 2024 IEXEC BLOCKCHAIN TECH <[email protected]>
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { AddressZero } from '@ethersproject/constants';
5+
import { loadFixture, mine } from '@nomicfoundation/hardhat-network-helpers';
6+
import { setNextBlockTimestamp } from '@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time';
7+
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
8+
import { expect } from 'hardhat';
9+
import { loadHardhatFixtureDeployment } from '../scripts/hardhat-fixture-deployer';
10+
import { IexecInterfaceNative, IexecInterfaceNative__factory } from '../typechain';
11+
import { OrdersActors, OrdersAssets, OrdersPrices, buildOrders } from '../utils/createOrders';
12+
13+
import {
14+
TaskStatusEnum,
15+
buildAndSignContributionAuthorizationMessage,
16+
buildResultHashAndResultSeal,
17+
buildUtf8ResultAndDigest,
18+
getIexecAccounts,
19+
} from '../utils/poco-tools';
20+
import { IexecWrapper } from './utils/IexecWrapper';
21+
22+
const standardDealTag = '0x0000000000000000000000000000000000000000000000000000000000000000';
23+
const appPrice = 1000;
24+
const datasetPrice = 1_000_000;
25+
const workerpoolPrice = 1_000_000_000;
26+
const { results, resultDigest } = buildUtf8ResultAndDigest('result');
27+
28+
let proxyAddress: string;
29+
let [iexecPoco, iexecPocoAsScheduler]: IexecInterfaceNative[] = [];
30+
let iexecWrapper: IexecWrapper;
31+
let [appAddress, workerpoolAddress, datasetAddress]: string[] = [];
32+
let [
33+
requester,
34+
appProvider,
35+
datasetProvider,
36+
scheduler,
37+
anyone,
38+
worker1,
39+
worker2,
40+
worker3,
41+
worker4,
42+
]: SignerWithAddress[] = [];
43+
let ordersActors: OrdersActors;
44+
let ordersAssets: OrdersAssets;
45+
let ordersPrices: OrdersPrices;
46+
47+
describe('Integration tests', function () {
48+
beforeEach('Deploy', async () => {
49+
// Deploy all contracts
50+
proxyAddress = await loadHardhatFixtureDeployment();
51+
// Initialize test environment
52+
await loadFixture(initFixture);
53+
});
54+
55+
async function initFixture() {
56+
const accounts = await getIexecAccounts();
57+
({
58+
requester,
59+
appProvider,
60+
datasetProvider,
61+
scheduler,
62+
anyone,
63+
worker1,
64+
worker2,
65+
worker3,
66+
worker4,
67+
} = accounts);
68+
iexecWrapper = new IexecWrapper(proxyAddress, accounts);
69+
({ appAddress, datasetAddress, workerpoolAddress } = await iexecWrapper.createAssets());
70+
iexecPoco = IexecInterfaceNative__factory.connect(proxyAddress, anyone);
71+
iexecPocoAsScheduler = iexecPoco.connect(scheduler);
72+
ordersActors = {
73+
appOwner: appProvider,
74+
datasetOwner: datasetProvider,
75+
workerpoolOwner: scheduler,
76+
requester: requester,
77+
};
78+
ordersAssets = {
79+
app: appAddress,
80+
dataset: datasetAddress,
81+
workerpool: workerpoolAddress,
82+
};
83+
ordersPrices = {
84+
app: appPrice,
85+
dataset: datasetPrice,
86+
workerpool: workerpoolPrice,
87+
};
88+
}
89+
90+
/*
91+
This test simulates the full lifecycle of a task in iExec:
92+
- Creates a deal with specific orders and initializes a task.
93+
- Tests worker contributions:
94+
- The first group of workers contributes, triggering the reveal phase.
95+
- Task is reopened after the reveal deadline passes.
96+
- Ensures that workers who already contributed cannot contribute again.
97+
- The second group of workers contributes and reveals successfully.
98+
- Finalizes the task, distributing rewards among workers and the scheduler.
99+
- Validates token balance changes for all participants.
100+
- Verifies that winning workers receive a positive score, while losing workers do not.
101+
*/
102+
it(`[1] Task lifecycle with contributions and reopening`, async function () {
103+
const volume = 1;
104+
const workers = [worker1, worker2, worker3, worker4];
105+
const firstContributors = workers.slice(0, 2);
106+
const secondContributors = workers.slice(2, 4);
107+
const accounts = [requester, scheduler, appProvider, datasetProvider, ...workers];
108+
109+
// Create deal.
110+
const orders = buildOrders({
111+
assets: ordersAssets,
112+
prices: ordersPrices,
113+
requester: requester.address,
114+
tag: standardDealTag,
115+
volume,
116+
trust: 4,
117+
});
118+
const { dealId, dealPrice, schedulerStakePerDeal } = await iexecWrapper.signAndMatchOrders(
119+
...orders.toArray(),
120+
);
121+
const taskPrice = appPrice + datasetPrice + workerpoolPrice;
122+
const schedulerStakePerTask = schedulerStakePerDeal / volume;
123+
const accountsInitialFrozens = await iexecWrapper.getInitialFrozens(accounts);
124+
125+
for (let i = 0; i < 4; i++) {
126+
expect(await iexecPoco.viewScore(workers[i].address)).to.be.equal(0);
127+
}
128+
const taskId = await iexecWrapper.initializeTask(dealId, 0);
129+
const workerStakePerTask = await iexecPoco
130+
.viewDeal(dealId)
131+
.then((deal) => deal.workerStake.toNumber());
132+
for (const contributor of firstContributors) {
133+
await iexecWrapper.contributeToTask(dealId, 0, resultDigest, contributor);
134+
}
135+
const task = await iexecPoco.viewTask(taskId);
136+
expect(task.status).to.equal(TaskStatusEnum.REVEALING);
137+
await setNextBlockTimestamp(task.revealDeadline).then(() => mine());
138+
await expect(iexecPocoAsScheduler.reopen(taskId))
139+
.to.emit(iexecPoco, 'TaskReopen')
140+
.withArgs(taskId);
141+
expect((await iexecPoco.viewTask(taskId)).status).to.equal(TaskStatusEnum.ACTIVE);
142+
143+
// test that the already contributed workers can't contribute anymore
144+
for (const contributor of firstContributors) {
145+
const { resultHash, resultSeal } = buildResultHashAndResultSeal(
146+
taskId,
147+
resultDigest,
148+
contributor,
149+
);
150+
const schedulerSignature = await buildAndSignContributionAuthorizationMessage(
151+
contributor.address,
152+
taskId,
153+
AddressZero,
154+
scheduler,
155+
);
156+
await expect(
157+
iexecPoco
158+
.connect(contributor)
159+
.contribute(
160+
taskId,
161+
resultHash,
162+
resultSeal,
163+
AddressZero,
164+
'0x',
165+
schedulerSignature,
166+
),
167+
).to.revertedWithoutReason();
168+
}
169+
170+
for (const contributor of secondContributors) {
171+
await iexecWrapper.contributeToTask(dealId, 0, resultDigest, contributor);
172+
}
173+
for (const contributor of secondContributors) {
174+
await iexecPoco
175+
.connect(contributor)
176+
.reveal(taskId, resultDigest)
177+
.then((tx) => tx.wait());
178+
}
179+
const finalizeTx = await iexecPocoAsScheduler.finalize(taskId, results, '0x');
180+
await finalizeTx.wait();
181+
const totalWorkerPoolReward =
182+
workerpoolPrice + workerStakePerTask * firstContributors.length; // bad workers lose their stake and add it to the pool price
183+
184+
const workersRewardPerTask = await iexecWrapper.computeWorkersRewardForCurrentTask(
185+
totalWorkerPoolReward,
186+
dealId,
187+
);
188+
const expectedWinningWorkerBalanceChange =
189+
workerStakePerTask + workersRewardPerTask / secondContributors.length;
190+
// compute expected scheduler reward for current task
191+
const schedulerRewardPerTask = totalWorkerPoolReward - workersRewardPerTask;
192+
193+
const expectedProxyBalanceChange = -(
194+
dealPrice +
195+
workerStakePerTask * workers.length +
196+
schedulerStakePerTask
197+
);
198+
await expect(finalizeTx).to.changeTokenBalances(
199+
iexecPoco,
200+
[proxyAddress, ...accounts],
201+
[
202+
expectedProxyBalanceChange,
203+
0,
204+
schedulerStakePerTask + schedulerRewardPerTask,
205+
appPrice,
206+
datasetPrice,
207+
...firstContributors.map(() => 0), // Workers
208+
...secondContributors.map(() => expectedWinningWorkerBalanceChange), // Workers
209+
],
210+
);
211+
expect((await iexecPoco.viewTask(taskId)).status).to.equal(TaskStatusEnum.COMPLETED);
212+
const expectedFrozenChanges = [
213+
0,
214+
-taskPrice,
215+
-schedulerStakePerTask,
216+
0,
217+
0,
218+
...workers.map(() => 0),
219+
];
220+
await iexecWrapper.checkFrozenChanges(accountsInitialFrozens, expectedFrozenChanges);
221+
222+
// checks on losing worker
223+
for (const contributor of firstContributors) {
224+
expect(await iexecPoco.viewScore(contributor.address)).to.be.equal(0);
225+
}
226+
// checks on winning workers
227+
for (const contributor of secondContributors) {
228+
expect(await iexecPoco.viewScore(contributor.address)).to.be.equal(1);
229+
}
230+
});
231+
});

0 commit comments

Comments
 (0)