Skip to content

Commit

Permalink
Migrate Fullchain tests to Hardhat and convert them to full integrati…
Browse files Browse the repository at this point in the history
…on tests (#156)
  • Loading branch information
jeremyjams authored Nov 12, 2024
2 parents 825209a + f231b9d commit 428d469
Show file tree
Hide file tree
Showing 7 changed files with 314 additions and 107 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## vNEXT

- Migrate integration test files to Typescript & Hardhat:
- 000_fullchain.js (#155)
- Remove `smock` from unit tests:
- IexecEscrow.v8 (#154, #155)
- IexecPocoDelegate (#149, #151)
Expand Down
File renamed without changes.
212 changes: 169 additions & 43 deletions test/000_fullchain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,54 +3,68 @@

import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { TypedDataDomain } from 'ethers';
import hre, { ethers, expect } from 'hardhat';
import { ethers, expect } from 'hardhat';
import { loadHardhatFixtureDeployment } from '../scripts/hardhat-fixture-deployer';
import { IexecAccessors, IexecAccessors__factory, IexecPocoAccessors__factory } from '../typechain';
import { IexecPoco1 } from '../typechain/contracts/modules/interfaces/IexecPoco1.v8.sol';
import { IexecPoco1__factory } from '../typechain/factories/contracts/modules/interfaces/IexecPoco1.v8.sol';
import {
IexecInterfaceNative,
IexecInterfaceNative__factory,
IexecPocoAccessors__factory,
} from '../typechain';
import {
OrdersActors,
OrdersAssets,
OrdersPrices,
buildOrders,
signOrders,
} from '../utils/createOrders';
import { getDealId, getIexecAccounts } from '../utils/poco-tools';
import {
PocoMode,
TaskStatusEnum,
buildResultCallbackAndDigest,
buildUtf8ResultAndDigest,
getDealId,
getIexecAccounts,
} from '../utils/poco-tools';
import { IexecWrapper } from './utils/IexecWrapper';

// +---------+-------------+-------------+-------------+----------+-----+----------------+
// | | Sponsorship | Replication | Beneficiary | Callback | BoT | Type |
// +---------+-------------+-------------+-------------+----------+-----+----------------+
// | [1] | ✔ | ✔ | ✔ | ✔ | ✔ | Standard |
// | [2] | x | ✔ | ✔ | ✔ | ✔ | Standard |
// | [3] | ✔ | x | ✔ | ✔ | ✔ | Standard,TEE |
// | [4] | x | x | ✔ | ✔ | ✔ | Standard,TEE |
// | [5] | x | x | x | x | x | Standard,TEE |
// +---------+-------------+-------------+----------+-----+-------------+----------------+

const standardDealTag = '0x0000000000000000000000000000000000000000000000000000000000000000';
const teeDealTag = '0x0000000000000000000000000000000000000000000000000000000000000001';
const taskIndex = 0;
const volume = taskIndex + 1;
const appPrice = 1000;
const datasetPrice = 1_000_000;
const workerpoolPrice = 1_000_000_000;
const callbackAddress = ethers.Wallet.createRandom().address;
const { results } = buildUtf8ResultAndDigest('result');
const { resultsCallback, callbackResultDigest } = buildResultCallbackAndDigest(123);

/*
* TODO make this a real integration test (match, contribute, ..., finalize).
*/

describe('IexecPoco (IT)', function () {
let domain: TypedDataDomain;
let proxyAddress: string;
let iexecAccessor: IexecAccessors;
let iexecPoco: IexecPoco1;
let iexecWrapper: IexecWrapper;
let [appAddress, workerpoolAddress, datasetAddress]: string[] = [];
let [
iexecAdmin,
requester,
sponsor,
beneficiary,
appProvider,
datasetProvider,
scheduler,
anyone,
]: SignerWithAddress[] = [];
let ordersActors: OrdersActors;
let ordersAssets: OrdersAssets;
let ordersPrices: OrdersPrices;
let proxyAddress: string;
let iexecPoco: IexecInterfaceNative;
let iexecWrapper: IexecWrapper;
let [appAddress, workerpoolAddress, datasetAddress]: string[] = [];
let [
requester,
sponsor,
beneficiary,
appProvider,
datasetProvider,
scheduler,
anyone,
worker1,
]: SignerWithAddress[] = [];
let ordersActors: OrdersActors;
let ordersAssets: OrdersAssets;
let ordersPrices: OrdersPrices;

describe('Integration tests', function () {
beforeEach('Deploy', async () => {
// Deploy all contracts
proxyAddress = await loadHardhatFixtureDeployment();
Expand All @@ -61,32 +75,24 @@ describe('IexecPoco (IT)', function () {
async function initFixture() {
const accounts = await getIexecAccounts();
({
iexecAdmin,
requester,
sponsor,
beneficiary,
appProvider,
datasetProvider,
scheduler,
anyone,
worker1,
} = accounts);
iexecWrapper = new IexecWrapper(proxyAddress, accounts);
({ appAddress, datasetAddress, workerpoolAddress } = await iexecWrapper.createAssets());
await iexecWrapper.setTeeBroker('0x0000000000000000000000000000000000000000');
iexecPoco = IexecPoco1__factory.connect(proxyAddress, iexecAdmin);
iexecAccessor = IexecAccessors__factory.connect(proxyAddress, anyone);
iexecPoco = IexecInterfaceNative__factory.connect(proxyAddress, anyone);
ordersActors = {
appOwner: appProvider,
datasetOwner: datasetProvider,
workerpoolOwner: scheduler,
requester: requester,
};
domain = {
name: 'iExecODB',
version: '5.0.0',
chainId: hre.network.config.chainId,
verifyingContract: proxyAddress,
};
ordersAssets = {
app: appAddress,
dataset: datasetAddress,
Expand All @@ -99,7 +105,112 @@ describe('IexecPoco (IT)', function () {
};
}

describe('MatchOrders', function () {
it('[1] Sponsorship, beneficiary, callback, BoT, replication', async function () {
const volume = 3;
// Create deal.
const orders = buildOrders({
assets: ordersAssets,
prices: ordersPrices,
requester: requester.address,
tag: standardDealTag,
beneficiary: beneficiary.address,
callback: callbackAddress,
volume,
trust: 1, // TODO use 5 workers.
});
const { dealId, dealPrice, schedulerStakePerDeal } =
await iexecWrapper.signAndSponsorMatchOrders(...orders.toArray());
const taskPrice = appPrice + datasetPrice + workerpoolPrice;
const schedulerStakePerTask = schedulerStakePerDeal / volume;
const workerRewardPerTask = await iexecWrapper.computeWorkerRewardPerTask(
dealId,
PocoMode.CLASSIC,
);
const schedulerRewardPerTask = workerpoolPrice - workerRewardPerTask;
// Check initial balances.
// TODO save initial balances and use them in for loop for comparison.
await checkBalancesAndFrozens({
proxyBalance: dealPrice + schedulerStakePerDeal,
accounts: [
{ signer: sponsor, balance: 0, frozen: dealPrice },
{ signer: requester, balance: 0, frozen: 0 },
{ signer: scheduler, balance: 0, frozen: schedulerStakePerDeal },
{ signer: appProvider, balance: 0, frozen: 0 },
{ signer: datasetProvider, balance: 0, frozen: 0 },
{ signer: worker1, balance: 0, frozen: 0 },
],
});
// Finalize each task and check balance changes.
for (let taskIndex = 0; taskIndex < volume; taskIndex++) {
const taskId = await iexecWrapper.initializeTask(dealId, taskIndex);
const { workerStakePerTask } = await iexecWrapper.contributeToTask(
dealId,
taskIndex,
callbackResultDigest,
worker1,
);
await iexecPoco
.connect(worker1)
.reveal(taskId, callbackResultDigest)
.then((tx) => tx.wait());
await iexecPoco
.connect(scheduler)
.finalize(taskId, results, resultsCallback)
.then((tx) => tx.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;
// For each task, balances change such as:
// - Sponsor
// - frozen: frozenBefore - taskPrice
// - Requester: no changes
// - Scheduler
// - balance: balanceBefore + taskStake + taskReward
// - frozen: frozenBefore - taskStake
// - App
// - balance: balance before + appPrice
// - Dataset
// - balance: balance before + datasetPrice
// - Worker:
// - balance: balance before + taskStake + taskReward
// - frozen: frozen before - taskStake
await checkBalancesAndFrozens({
proxyBalance:
dealPrice +
schedulerStakePerDeal -
(taskPrice + schedulerStakePerTask) * completedTasks,
accounts: [
{ signer: sponsor, balance: 0, frozen: dealPrice - taskPrice * completedTasks },
{ signer: requester, balance: 0, frozen: 0 },
{
signer: scheduler,
balance: (schedulerStakePerTask + schedulerRewardPerTask) * completedTasks,
frozen: schedulerStakePerDeal - schedulerStakePerTask * completedTasks,
},
{ signer: appProvider, balance: appPrice * completedTasks, frozen: 0 },
{ signer: datasetProvider, balance: datasetPrice * completedTasks, frozen: 0 },
{
signer: worker1,
balance: (workerStakePerTask + workerRewardPerTask) * completedTasks,
frozen: 0,
},
],
});
}
});

// TODO implement the following tests.

it('[2] No sponsorship, beneficiary, callback, BoT, replication', async function () {});

it('[3] Sponsorship, beneficiary, callback, BoT, no replication', async function () {});

it('[4] No sponsorship, beneficiary, callback, BoT, no replication', async function () {});

it('[5] No sponsorship, no beneficiary, no callback, no BoT, no replication', async function () {});

describe.skip('MatchOrders', function () {
it('Should sponsor match orders (TEE)', async function () {
const callbackAddress = ethers.Wallet.createRandom().address;
const orders = buildOrders({
Expand Down Expand Up @@ -166,3 +277,18 @@ describe('IexecPoco (IT)', function () {
});
});
});

async function checkBalancesAndFrozens(args: {
proxyBalance: number;
accounts: { signer: SignerWithAddress; balance: number; frozen: number }[];
}) {
expect(await iexecPoco.balanceOf(proxyAddress)).to.equal(args.proxyBalance);
for (const account of args.accounts) {
const message = `Failed with account at index ${args.accounts.indexOf(account)}`;
expect(await iexecPoco.balanceOf(account.signer.address)).to.equal(
account.balance,
message,
);
expect(await iexecPoco.frozenOf(account.signer.address)).to.equal(account.frozen, message);
}
}
53 changes: 11 additions & 42 deletions test/byContract/IexecAccessors/IexecAccessors.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: 2020-2024 IEXEC BLOCKCHAIN TECH <contact@iex.ec>
// SPDX-License-Identifier: Apache-2.0

import { AddressZero, HashZero } from '@ethersproject/constants';
import { HashZero } from '@ethersproject/constants';
import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { deployments, ethers, expect } from 'hardhat';
Expand All @@ -14,9 +14,7 @@ import {
import { OrdersAssets, buildOrders } from '../../../utils/createOrders';
import {
TaskStatusEnum,
buildAndSignContributionAuthorizationMessage,
buildResultCallbackAndDigest,
buildResultHashAndResultSeal,
buildUtf8ResultAndDigest,
getIexecAccounts,
getTaskId,
Expand Down Expand Up @@ -87,7 +85,7 @@ describe('IexecAccessors', async () => {

it('viewTask', async function () {
const { dealId, taskId, taskIndex, startTime, timeRef } = await createDeal();
await initializeTask(dealId, taskIndex);
await iexecWrapper.initializeTask(dealId, taskIndex);

const contributionDeadlineRatio = (
await iexecPoco.contribution_deadline_ratio()
Expand Down Expand Up @@ -186,9 +184,11 @@ describe('IexecAccessors', async () => {
it('Should get result of task', async function () {
const { dealId, taskId, taskIndex } = await createDeal();

await initializeTask(dealId, taskIndex).then(() =>
contributeToTask(dealId, taskIndex, callbackResultDigest),
);
await iexecWrapper
.initializeTask(dealId, taskIndex)
.then(() =>
iexecWrapper.contributeToTask(dealId, taskIndex, callbackResultDigest, worker1),
);
await iexecPoco
.connect(worker1)
.reveal(taskId, callbackResultDigest)
Expand All @@ -206,10 +206,10 @@ describe('IexecAccessors', async () => {
const { dealId } = await createDeal(3);

const unsetTaskId = getTaskId(dealId, 0);
const activeTaskId = await initializeTask(dealId, 1);
const revealingTaskId = await initializeTask(dealId, 2).then(() =>
contributeToTask(dealId, 2, resultDigest),
);
const activeTaskId = await iexecWrapper.initializeTask(dealId, 1);
const { taskId: revealingTaskId } = await iexecWrapper
.initializeTask(dealId, 2)
.then(() => iexecWrapper.contributeToTask(dealId, 2, resultDigest, worker1));

await verifyTaskStatusAndResult(unsetTaskId, TaskStatusEnum.UNSET);
await verifyTaskStatusAndResult(activeTaskId, TaskStatusEnum.ACTIVE);
Expand All @@ -236,37 +236,6 @@ async function createDeal(volume: number = 1) {
return { dealId, taskId, taskIndex, startTime, timeRef };
}

/**
* Helper function to initialize a task.
*/
async function initializeTask(dealId: string, taskIndex: number) {
await iexecPoco.initialize(dealId, taskIndex).then((tx) => tx.wait());
return getTaskId(dealId, taskIndex);
}

/**
* Helper function to contribute to a task.
*/
async function contributeToTask(dealId: string, taskIndex: number, resultDigest: string) {
const taskId = getTaskId(dealId, taskIndex);
const workerTaskStake = await iexecPoco
.viewDeal(dealId)
.then((deal) => deal.workerStake.toNumber());
const { resultHash, resultSeal } = buildResultHashAndResultSeal(taskId, resultDigest, worker1);
const schedulerSignature = await buildAndSignContributionAuthorizationMessage(
worker1.address,
taskId,
AddressZero,
scheduler,
);
await iexecWrapper.depositInIexecAccount(worker1, workerTaskStake);
await iexecPoco
.connect(worker1)
.contribute(taskId, resultHash, resultSeal, AddressZero, '0x', schedulerSignature)
.then((tx) => tx.wait());
return taskId;
}

async function verifyTaskStatusAndResult(taskId: string, expectedStatus: number) {
const task = await iexecPoco.viewTask(taskId);
expect(task.status).to.equal(expectedStatus);
Expand Down
Loading

0 comments on commit 428d469

Please sign in to comment.