Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate 300 fullchain reopen file #170

Merged
merged 9 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- 200_fullchain-bot.js (#164, #166)
- 201_fullchain-bot-dualPool.js (#171, #172)
- Fix balance checks in integration tests (#165)
- 300_fullchain-reopen.js (#170)
- Remove `smock` from unit tests:
- IexecEscrow.v8 (#154, #155)
- IexecPocoDelegate (#149, #151)
Expand Down
File renamed without changes.
222 changes: 222 additions & 0 deletions test/300_fullchain-reopen.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
// SPDX-FileCopyrightText: 2024 IEXEC BLOCKCHAIN TECH <[email protected]>
// SPDX-License-Identifier: Apache-2.0

import { AddressZero } from '@ethersproject/constants';
import { loadFixture, mine } from '@nomicfoundation/hardhat-network-helpers';
import { setNextBlockTimestamp } from '@nomicfoundation/hardhat-network-helpers/dist/src/helpers/time';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { expect } from 'hardhat';
import { loadHardhatFixtureDeployment } from '../scripts/hardhat-fixture-deployer';
import { IexecInterfaceNative, IexecInterfaceNative__factory } from '../typechain';
import { OrdersActors, OrdersAssets, OrdersPrices, buildOrders } from '../utils/createOrders';

import {
TaskStatusEnum,
buildAndSignContributionAuthorizationMessage,
buildResultHashAndResultSeal,
buildUtf8ResultAndDigest,
getIexecAccounts,
} from '../utils/poco-tools';
import { IexecWrapper } from './utils/IexecWrapper';

const standardDealTag = '0x0000000000000000000000000000000000000000000000000000000000000000';
const appPrice = 1000;
const datasetPrice = 1_000_000;
const workerpoolPrice = 1_000_000_000;
const { results, resultDigest } = buildUtf8ResultAndDigest('result');

let proxyAddress: string;
let [iexecPoco, iexecPocoAsScheduler]: IexecInterfaceNative[] = [];
let iexecWrapper: IexecWrapper;
let [appAddress, workerpoolAddress, datasetAddress]: string[] = [];
let [
requester,
appProvider,
datasetProvider,
scheduler,
anyone,
worker1,
worker2,
worker3,
worker4,
]: SignerWithAddress[] = [];
let ordersActors: OrdersActors;
let ordersAssets: OrdersAssets;
let ordersPrices: OrdersPrices;

describe('Integration tests', function () {
beforeEach('Deploy', async () => {
// Deploy all contracts
proxyAddress = await loadHardhatFixtureDeployment();
// Initialize test environment
await loadFixture(initFixture);
});

async function initFixture() {
const accounts = await getIexecAccounts();
({
requester,
appProvider,
datasetProvider,
scheduler,
anyone,
worker1,
worker2,
worker3,
worker4,
} = accounts);
iexecWrapper = new IexecWrapper(proxyAddress, accounts);
({ appAddress, datasetAddress, workerpoolAddress } = await iexecWrapper.createAssets());
iexecPoco = IexecInterfaceNative__factory.connect(proxyAddress, anyone);
iexecPocoAsScheduler = iexecPoco.connect(scheduler);
ordersActors = {
appOwner: appProvider,
datasetOwner: datasetProvider,
workerpoolOwner: scheduler,
requester: requester,
};
ordersAssets = {
app: appAddress,
dataset: datasetAddress,
workerpool: workerpoolAddress,
};
ordersPrices = {
app: appPrice,
dataset: datasetPrice,
workerpool: workerpoolPrice,
};
}

/*
This test simulates the full lifecycle of a task in iExec:
- Creates a deal with specific orders and initializes a task.
- Tests worker contributions:
- The first group of workers contributes, triggering the reveal phase.
- Task is reopened after the reveal deadline passes.
- Ensures that workers who already contributed cannot contribute again.
- The second group of workers contributes and reveals successfully.
- Finalizes the task, distributing rewards among workers and the scheduler.
- Validates token balance changes for all participants.
- Verifies that winning workers receive a positive score, while losing workers do not.
*/
it(`[1] Task lifecycle with contributions and reopening`, async function () {
const volume = 1;
const workers = [worker1, worker2, worker3, worker4];
const firstContribution = workers.slice(0, 2);
const secondContribution = workers.slice(2, 4);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const firstContribution = workers.slice(0, 2);
const secondContribution = workers.slice(2, 4);
const firstContributors = workers.slice(0, 2);
const secondContributors = workers.slice(2, 4);

const accounts = [requester, scheduler, appProvider, datasetProvider, ...workers];

// Create deal.
const orders = buildOrders({
assets: ordersAssets,
prices: ordersPrices,
requester: requester.address,
tag: standardDealTag,
volume,
trust: 4,
});
const { dealId, dealPrice, schedulerStakePerDeal } = await iexecWrapper.signAndMatchOrders(
...orders.toArray(),
);
const taskPrice = appPrice + datasetPrice + workerpoolPrice;
const schedulerStakePerTask = schedulerStakePerDeal / volume;
const accountsInitialFrozens = await iexecWrapper.getInitialFrozens(accounts);

for (let i = 0; i < 4; i++) {
expect(await iexecPoco.viewScore(workers[i].address)).to.be.equal(0);
}
const taskId = await iexecWrapper.initializeTask(dealId, 0);
const workerStakePerTask = await iexecPoco
.viewDeal(dealId)
.then((deal) => deal.workerStake.toNumber());
for (const contributor of firstContribution) {
await iexecWrapper.contributeToTask(dealId, 0, resultDigest, contributor);
}
const task = await iexecPoco.viewTask(taskId);
expect(task.status).to.equal(TaskStatusEnum.REVEALING);
await setNextBlockTimestamp(task.revealDeadline).then(() => mine());
await expect(iexecPocoAsScheduler.reopen(taskId))
.to.emit(iexecPoco, 'TaskReopen')
.withArgs(taskId);
expect((await iexecPoco.viewTask(taskId)).status).to.equal(TaskStatusEnum.ACTIVE);
// test that the already contributed worker 1 can't contribute anymore
const { resultHash, resultSeal } = buildResultHashAndResultSeal(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for (firstContributors) {
  expect.reverted
}

taskId,
resultDigest,
worker1,
);
const schedulerSignature = await buildAndSignContributionAuthorizationMessage(
worker1.address,
taskId,
AddressZero,
scheduler,
);
await expect(
iexecPoco
.connect(worker1)
.contribute(taskId, resultHash, resultSeal, AddressZero, '0x', schedulerSignature),
).to.revertedWithoutReason();

for (const contributor of secondContribution) {
await iexecWrapper.contributeToTask(dealId, 0, resultDigest, contributor);
}
for (const contributor of secondContribution) {
await iexecPoco
.connect(contributor)
.reveal(taskId, resultDigest)
.then((tx) => tx.wait());
}
const finalizeTx = await iexecPocoAsScheduler.finalize(taskId, results, '0x');
await finalizeTx.wait();
const totalWorkerPoolReward =
workerpoolPrice + workerStakePerTask * firstContribution.length; // bad wrokers lose their stake and add it to the pool price
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
workerpoolPrice + workerStakePerTask * firstContribution.length; // bad wrokers lose their stake and add it to the pool price
workerpoolPrice + workerStakePerTask * firstContribution.length; // bad workers lose their stake and add it to the pool price


const workersRewardPerTask = await iexecWrapper.computeWorkersRewardForCurrentTask(
totalWorkerPoolReward,
dealId,
);
const expectedWinningWorkerBalanceChange =
workerStakePerTask + workersRewardPerTask / secondContribution.length;
// compute expected scheduler reward for current task
const schedulerRewardPerTask = totalWorkerPoolReward - workersRewardPerTask;
const expectedSchedulerBalanceChange = schedulerStakePerTask + schedulerRewardPerTask;

const expectedProxyBalanceChange = -(
dealPrice +
workerStakePerTask * workers.length +
schedulerStakePerTask
);
await expect(finalizeTx).to.changeTokenBalances(
iexecPoco,
[proxyAddress, ...accounts],
[
expectedProxyBalanceChange,
0,
expectedSchedulerBalanceChange,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
expectedSchedulerBalanceChange,
schedulerStakePerTask + schedulerRewardPerTask,

appPrice,
datasetPrice,
...firstContribution.map(() => 0), // Workers
...secondContribution.map(() => expectedWinningWorkerBalanceChange), // Workers
],
);
expect((await iexecPoco.viewTask(taskId)).status).to.equal(TaskStatusEnum.COMPLETED);
const expectedFrozenChanges = [
0,
-taskPrice,
-schedulerStakePerTask,
0,
0,
...workers.map(() => 0),
];
await iexecWrapper.checkFrozenChanges(accountsInitialFrozens, expectedFrozenChanges);

// checks on losing worker
for (const contributor of firstContribution) {
expect(await iexecPoco.viewScore(contributor.address)).to.be.equal(0);
}
// checks on winning workers
for (const contributor of secondContribution) {
expect(await iexecPoco.viewScore(contributor.address)).to.be.equal(1);
}
});
});
Loading