Skip to content

Commit

Permalink
migrate 200 IT file
Browse files Browse the repository at this point in the history
  • Loading branch information
gfournieriExec committed Dec 2, 2024
1 parent 45da3e4 commit 71e22fa
Show file tree
Hide file tree
Showing 2 changed files with 288 additions and 0 deletions.
File renamed without changes.
288 changes: 288 additions & 0 deletions test/200_fullchain-bot.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,288 @@
// SPDX-FileCopyrightText: 2024 IEXEC BLOCKCHAIN TECH <[email protected]>
// SPDX-License-Identifier: Apache-2.0

import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
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 {
PocoMode,
TaskStatusEnum,
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;

let proxyAddress: string;
let iexecPoco: IexecInterfaceNative;
let iexecWrapper: IexecWrapper;
let [appAddress, workerpoolAddress, datasetAddress]: string[] = [];
let [
requester,
appProvider,
datasetProvider,
scheduler,
anyone,
worker1,
worker2,
worker3,
worker4,
worker5,
]: 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,
worker5,
} = accounts);
iexecWrapper = new IexecWrapper(proxyAddress, accounts);
({ appAddress, datasetAddress, workerpoolAddress } = await iexecWrapper.createAssets());
iexecPoco = IexecInterfaceNative__factory.connect(proxyAddress, anyone);
ordersActors = {
appOwner: appProvider,
datasetOwner: datasetProvider,
workerpoolOwner: scheduler,
requester: requester,
};
ordersAssets = {
app: appAddress,
dataset: datasetAddress,
workerpool: workerpoolAddress,
};
ordersPrices = {
app: appPrice,
dataset: datasetPrice,
workerpool: workerpoolPrice,
};
}

it('Task Lifecycle with BoT Replication and Error Handling', async function () {
const workers = [worker1, worker2, worker3, worker4, worker5];
// Create deal.
const volume = 3;
const tasksAndWorkers: {
[key: number]: {
worker: SignerWithAddress;
useEnclave: boolean;
result: string;
}[];
} = {
0: [
{ worker: worker1, useEnclave: false, result: 'iExec BOT 0' },
{ worker: worker2, useEnclave: false, result: 'iExec BOT 0' },
],
1: [
{ worker: worker2, useEnclave: true, result: 'iExec BOT 1' },
{ worker: worker3, useEnclave: true, result: 'iExec BOT 1' },
],
2: [
{ worker: worker1, useEnclave: false, result: 'iExec BOT 2' },
{ worker: worker3, useEnclave: false, result: '<timeout reached>' },
{ worker: worker2, useEnclave: true, result: 'iExec BOT 2' },
{ worker: worker4, useEnclave: true, result: 'iExec BOT 2' },
{ worker: worker5, useEnclave: true, result: 'iExec BOT 2' },
],
};
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 workerRewardPerTask = await iexecWrapper.computeWorkerRewardPerTask(
dealId,
PocoMode.CLASSIC,
);
const schedulerRewardPerTask = workerpoolPrice - workerRewardPerTask;
// Save frozens

const accounts = [requester, scheduler, appProvider, datasetProvider, ...workers];
const accountsInitialFrozens = await getInitialFrozens(accounts);
// Track initial scores
// Finalize each task and check balance changes.
const workerStakePerTask = await iexecPoco
.viewDeal(dealId)
.then((deal) => deal.workerStake.toNumber());
for (let taskIndex = 0; taskIndex < volume; taskIndex++) {
const taskId = await iexecWrapper.initializeTask(dealId, taskIndex);
const initialScores = await getInitialScores(workers);
const contributions = tasksAndWorkers[taskIndex];
for (const contribution of contributions) {
const { worker, useEnclave, result } = contribution;
const { resultDigest } = buildUtf8ResultAndDigest(result);

if (useEnclave) {
await iexecWrapper.contributeToTeeTask(dealId, taskIndex, resultDigest, worker);
} else {
await iexecWrapper.contributeToTask(dealId, taskIndex, resultDigest, worker);
}
}
// Reveal contributions for all workers
for (const contribution of contributions) {
const { worker, result } = contribution;
const { resultDigest } = buildUtf8ResultAndDigest(result);
if (result !== '<timeout reached>') {
await iexecPoco
.connect(worker)
.reveal(taskId, resultDigest)
.then((tx) => tx.wait());
}
}
const { results } = buildUtf8ResultAndDigest(tasksAndWorkers[taskIndex][0].result);
const finalizeTx = await iexecPoco.connect(scheduler).finalize(taskId, results, '0x');
await finalizeTx.wait();
expect((await iexecPoco.viewTask(taskId)).status).to.equal(TaskStatusEnum.COMPLETED);
// Multiply amount by the number of finalized tasks to correctly compute
// stake and reward amounts.
const completedTasks = taskIndex + 1;
// Verify token balance changes
const expectedProxyBalanceChange = -(
taskPrice * completedTasks +
schedulerStakePerTask * completedTasks
);
const winningWorkers = contributions
.filter(
(contribution) => contribution.result === 'iExec BOT ' + taskIndex.toString(),
)
.map((contribution) => contribution.worker);
const loosingWorkers = contributions
.filter(
(contribution) => contribution.result !== 'iExec BOT ' + taskIndex.toString(),
)
.map((contribution) => contribution.worker);
const nonParticipantWorkers = workers.filter(
(worker) => !winningWorkers.includes(worker) && !loosingWorkers.includes(worker),
);
const expectedWorkerBalanceChange =
workerStakePerTask + workerRewardPerTask / winningWorkers.length;
expect(finalizeTx).to.changeTokenBalances(
iexecPoco,
[proxyAddress, ...accounts],
[
expectedProxyBalanceChange, // Proxy
-taskPrice * completedTasks, // Requester
schedulerStakePerTask + schedulerRewardPerTask, // Scheduler
appPrice, // AppProvider
datasetPrice, // DatasetProvider
...winningWorkers.map(() => expectedWorkerBalanceChange), // winning workers
...loosingWorkers.map(() => 0), // loosing workers
...nonParticipantWorkers.map(() => 0), // non participant workers
],
);
// Calculate expected frozen changes
const expectedFrozenChanges = [
0, // Proxy
-taskPrice * completedTasks, // Requester
-schedulerStakePerTask * completedTasks, // Scheduler
0, // AppProvider
0, // DatasetProvider
...workers.map(() => 0), // Add 0 for each worker
];
await checkFrozenChanges(accountsInitialFrozens, expectedFrozenChanges);
await validateScores(
initialScores,
winningWorkers,
loosingWorkers,
nonParticipantWorkers,
);
}
});
async function getInitialFrozens(accounts: SignerWithAddress[]) {
let initialFrozens = [
{
address: proxyAddress,
frozen: (await iexecPoco.frozenOf(proxyAddress)).toNumber(),
},
];
for (const account of accounts) {
initialFrozens.push({
address: account.address,
frozen: (await iexecPoco.frozenOf(account.address)).toNumber(),
});
}
return initialFrozens;
}

async function getInitialScores(
workers: SignerWithAddress[],
): Promise<{ [address: string]: number }> {
const scores: { [address: string]: number } = {};
for (const worker of workers) {
scores[worker.address] = (await iexecPoco.viewScore(worker.address)).toNumber();
}
return scores;
}
});

async function checkFrozenChanges(
accountsInitialFrozens: { address: string; frozen: number }[],
expectedFrozenChanges: number[],
) {
for (let i = 0; i < accountsInitialFrozens.length; i++) {
const message = `Failed with account at index ${i}`;
expect(await iexecPoco.frozenOf(accountsInitialFrozens[i].address)).to.equal(
accountsInitialFrozens[i].frozen + expectedFrozenChanges[i],
message,
);
}
}

async function validateScores(
initialScores: { [address: string]: number },
winningWorkers: SignerWithAddress[],
loosingWorkers: SignerWithAddress[],
nonParticipantWorkers: SignerWithAddress[],
) {
for (const winningWorker of winningWorkers) {
const currentScore = (await iexecPoco.viewScore(winningWorker.address)).toNumber();
expect(currentScore, `Worker ${winningWorker.address} score mismatch`).to.equal(
initialScores[winningWorker.address] + 1,
);
}
for (const loosingWorker of loosingWorkers) {
const currentScore = (await iexecPoco.viewScore(loosingWorker.address)).toNumber();
expect(currentScore, `Worker ${loosingWorker.address} score mismatch`).to.equal(
initialScores[loosingWorker.address] - 1,
);
}
for (const nonParticipantWorker of nonParticipantWorkers) {
const currentScore = (await iexecPoco.viewScore(nonParticipantWorker.address)).toNumber();
expect(currentScore, `Worker ${nonParticipantWorker.address} score mismatch`).to.equal(
initialScores[nonParticipantWorker.address],
);
}
}

0 comments on commit 71e22fa

Please sign in to comment.