Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -15,6 +15,7 @@
- 000_fullchain-5workers-1error.js (#160, #162)
- Clean ToDo (#163)
- 200_fullchain-bot.js (#164, #166)
- 201_fullchain-bot-dualPool.js (#171)
- Fix balance checks in integration tests (#165)
- Remove `smock` from unit tests:
- IexecEscrow.v8 (#154, #155)
Expand Down
17 changes: 10 additions & 7 deletions test/000_fullchain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,9 @@ describe('Integration tests', function () {
volume,
trust: workers.length ** 2 - 1,
});
const { dealId, dealPrice, schedulerStakePerDeal } =
await iexecWrapper.signAndSponsorMatchOrders(...orders.toArray());
const { dealId, schedulerStakePerDeal } = await iexecWrapper.signAndSponsorMatchOrders(
...orders.toArray(),
);
const taskPrice = appPrice + datasetPrice + workerpoolPrice;
const schedulerStakePerTask = schedulerStakePerDeal / volume;
const workersRewardPerTask = await iexecWrapper.computeWorkersRewardPerTask(
Expand Down Expand Up @@ -209,7 +210,7 @@ describe('Integration tests', function () {
volume,
trust: workers.length ** 2 - 1,
});
const { dealId, dealPrice, schedulerStakePerDeal } = await iexecWrapper.signAndMatchOrders(
const { dealId, schedulerStakePerDeal } = await iexecWrapper.signAndMatchOrders(
...orders.toArray(),
);
const taskPrice = appPrice + datasetPrice + workerpoolPrice;
Expand Down Expand Up @@ -298,8 +299,9 @@ describe('Integration tests', function () {
volume,
trust: 1,
});
const { dealId, dealPrice, schedulerStakePerDeal } =
await iexecWrapper.signAndSponsorMatchOrders(...orders.toArray());
const { dealId, schedulerStakePerDeal } = await iexecWrapper.signAndSponsorMatchOrders(
...orders.toArray(),
);
const taskPrice = appPrice + datasetPrice + workerpoolPrice;
const schedulerStakePerTask = schedulerStakePerDeal / volume;
const workersRewardPerTask = await iexecWrapper.computeWorkersRewardPerTask(
Expand Down Expand Up @@ -376,7 +378,7 @@ describe('Integration tests', function () {
volume,
trust: 1,
});
const { dealId, dealPrice, schedulerStakePerDeal } = await iexecWrapper.signAndMatchOrders(
const { dealId, schedulerStakePerDeal } = await iexecWrapper.signAndMatchOrders(
...orders.toArray(),
);
const taskPrice = appPrice + datasetPrice + workerpoolPrice;
Expand Down Expand Up @@ -601,7 +603,8 @@ describe('Integration tests', function () {
});
}
});
it(`[7] No sponsorship, no beneficiary, no callback, no BoT, up to 5 workers with 1 bad worker`, async function () {

it('[7] No sponsorship, no beneficiary, no callback, no BoT, up to 5 workers with 1 bad worker', async function () {
const volume = 1;
const allWorkers = [worker1, worker2, worker3, worker4, worker5];
const { resultDigest: badResultDigest } = buildUtf8ResultAndDigest('bad-result');
Expand Down
255 changes: 255 additions & 0 deletions test/201_fullchain-multi-orders.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
// SPDX-FileCopyrightText: 2024 IEXEC BLOCKCHAIN TECH <contact@iex.ec>
// SPDX-License-Identifier: Apache-2.0

import { loadFixture } from '@nomicfoundation/hardhat-network-helpers';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { ethers, expect } from 'hardhat';
import { loadHardhatFixtureDeployment } from '../scripts/hardhat-fixture-deployer';
import {
IexecInterfaceNative,
IexecInterfaceNative__factory,
IexecPocoAccessors,
IexecPocoAccessors__factory,
} from '../typechain';
import {
IexecOrders,
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 workerpoolPrice1 = 1_000_000_015;
const workerpoolPrice2 = 1_000_000_025;
const { results, resultDigest } = buildUtf8ResultAndDigest('result');

let proxyAddress: string;
let iexecPoco: IexecInterfaceNative;
let iexecPocoAccessors: IexecPocoAccessors; // To use `computeDealVolume()`
let iexecWrapper: IexecWrapper;
let [appAddress, workerpoolAddress, datasetAddress]: string[] = [];
let [requester, 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();
// Initialize test environment
await loadFixture(initFixture);
});

async function initFixture() {
const accounts = await getIexecAccounts();
({ requester, appProvider, datasetProvider, scheduler, anyone, worker1 } = accounts);
iexecWrapper = new IexecWrapper(proxyAddress, accounts);
({ appAddress, datasetAddress, workerpoolAddress } = await iexecWrapper.createAssets());
iexecPoco = IexecInterfaceNative__factory.connect(proxyAddress, anyone);
iexecPocoAccessors = IexecPocoAccessors__factory.connect(proxyAddress, ethers.provider);
ordersActors = {
appOwner: appProvider,
datasetOwner: datasetProvider,
workerpoolOwner: scheduler,
requester: requester,
};
ordersAssets = {
app: appAddress,
dataset: datasetAddress,
workerpool: workerpoolAddress,
};
ordersPrices = {
app: appPrice,
dataset: datasetPrice,
workerpool: 0, // Overridden below.
};
}

/**
* A test to run full workflow (matchOrders..finalize) with 2 orders having 2 different volumes
* for the same workerpool and only 1 request order.
*/
it('[1] No sponsorship, no beneficiary, no callback, BoT, no replication, 2 workerpool orders', async function () {
const volume = 3;
const workerpoolOrderVolume1 = 2;
const workerpoolOrderVolume2 = 10;
const dealVolume1 = Math.min(workerpoolOrderVolume1, volume); // min(2, 3);
const dealVolume2 = Math.min(workerpoolOrderVolume2, volume - dealVolume1); // min(10, 1)
const taskPrice1 = appPrice + datasetPrice + workerpoolPrice1;
const taskPrice2 = appPrice + datasetPrice + workerpoolPrice2;
// Create default orders.
const {
appOrder,
datasetOrder,
workerpoolOrder,
requesterOrder: requestOrder,
} = buildOrders({
assets: ordersAssets,
prices: ordersPrices,
requester: requester.address,
tag: standardDealTag,
volume,
}).toObject();
// Create 2 different orders for the same workerpool.
const workerpoolOrder1 = { ...workerpoolOrder }; // Shallow cloning is fine here.
const workerpoolOrder2 = { ...workerpoolOrder };
workerpoolOrder1.volume = workerpoolOrderVolume1;
workerpoolOrder1.workerpoolprice = workerpoolPrice1;
workerpoolOrder2.volume = workerpoolOrderVolume2;
workerpoolOrder2.workerpoolprice = workerpoolPrice2;
requestOrder.workerpoolmaxprice = Math.max(workerpoolPrice1, workerpoolPrice2);
// Match both workerpool orders with the same request order.
const dealOrders1 = new IexecOrders(
appOrder,
datasetOrder,
workerpoolOrder1,
requestOrder,
).toArray();
const dealOrders2 = new IexecOrders(
appOrder,
datasetOrder,
workerpoolOrder2,
requestOrder,
).toArray();
expect(await iexecPocoAccessors.computeDealVolume(...dealOrders1)).to.equal(dealVolume1);
const {
dealId: dealId1,
taskIndex: taskIndex1,
schedulerStakePerDeal: schedulerStakeForDeal1,
} = await iexecWrapper.signAndMatchOrders(
appOrder,
datasetOrder,
workerpoolOrder1,
requestOrder,
); // First task index is 0.
expect(await iexecPocoAccessors.computeDealVolume(...dealOrders2)).to.equal(dealVolume2);
const {
dealId: dealId2,
taskIndex: taskIndex2,
schedulerStakePerDeal: schedulerStakeForDeal2,
} = await iexecWrapper.signAndMatchOrders(
appOrder,
datasetOrder,
workerpoolOrder2,
requestOrder,
); // First task index is 2.
const deal1 = await iexecPoco.viewDeal(dealId1);
expect(deal1.botFirst).to.equal(0);
expect(deal1.botSize).to.equal(dealVolume1);
const deal2 = await iexecPoco.viewDeal(dealId2);
expect(deal2.botFirst).to.equal(dealVolume1);
expect(deal2.botSize).to.equal(dealVolume2);
// Compute stakes and rewards for each deal.
const schedulerStakePerTaskOfDeal1 = schedulerStakeForDeal1 / dealVolume1;
const schedulerStakePerTaskOfDeal2 = schedulerStakeForDeal2 / dealVolume2;
const workersRewardPerTaskOfDeal1 = await iexecWrapper.computeWorkersRewardPerTask(
dealId1,
PocoMode.CLASSIC,
);
const workersRewardPerTaskOfDeal2 = await iexecWrapper.computeWorkersRewardPerTask(
dealId2,
PocoMode.CLASSIC,
);
const schedulerRewardPerTaskOfDeal1 = workerpoolPrice1 - workersRewardPerTaskOfDeal1;
const schedulerRewardPerTaskOfDeal2 = workerpoolPrice2 - workersRewardPerTaskOfDeal2;
// Finalize each task and run checks.
await runTaskThenCheckBalancesAndVolumes(
dealId1,
taskIndex1,
taskPrice1,
schedulerStakePerTaskOfDeal1,
schedulerRewardPerTaskOfDeal1,
workersRewardPerTaskOfDeal1,
);
await runTaskThenCheckBalancesAndVolumes(
dealId1,
taskIndex1 + 1,
taskPrice1,
schedulerStakePerTaskOfDeal1,
schedulerRewardPerTaskOfDeal1,
workersRewardPerTaskOfDeal1,
);
await runTaskThenCheckBalancesAndVolumes(
dealId2,
taskIndex2,
taskPrice2,
schedulerStakePerTaskOfDeal2,
schedulerRewardPerTaskOfDeal2,
workersRewardPerTaskOfDeal2,
);
// Check remaining volumes.
expect(await iexecPoco.viewConsumed(iexecWrapper.hashOrder(requestOrder))).to.equal(volume);
expect(await iexecPoco.viewConsumed(iexecWrapper.hashOrder(workerpoolOrder1))).to.equal(
dealVolume1,
);
expect(await iexecPoco.viewConsumed(iexecWrapper.hashOrder(workerpoolOrder2))).to.equal(
dealVolume2,
);
});

async function runTaskThenCheckBalancesAndVolumes(
dealId: string,
taskIndex: number,
taskPrice: number,
schedulerStake: number,
schedulerReward: number,
workerReward: number,
) {
// Save frozens before task execution.
const accounts = [requester, scheduler, appProvider, datasetProvider, worker1];
const accountsInitialFrozens = await iexecWrapper.getInitialFrozens(accounts);
// Run task.
const taskId = await iexecWrapper.initializeTask(dealId, taskIndex);
const { workerStakePerTask: workerStake } = await iexecWrapper.contributeToTask(
dealId,
taskIndex,
resultDigest,
worker1,
);
await iexecPoco
.connect(worker1)
.reveal(taskId, resultDigest)
.then((tx) => tx.wait());
const finalizeTx = await iexecPoco.connect(scheduler).finalize(taskId, results, '0x');
await finalizeTx.wait();
// Check task.
const task = await iexecPoco.viewTask(taskId);
expect(task.status).to.equal(TaskStatusEnum.COMPLETED);
expect(task.idx).to.equal(taskIndex);
// Verify token balance changes.
const expectedProxyBalanceChange = -(taskPrice + schedulerStake + workerStake);
await expect(finalizeTx).to.changeTokenBalances(
iexecPoco,
[proxyAddress, requester, scheduler, appProvider, datasetProvider, worker1],
[
expectedProxyBalanceChange, // Proxy
0, // Requester
schedulerStake + schedulerReward, // Scheduler
appPrice, // AppProvider
datasetPrice, // DatasetProvider
workerStake + workerReward, // Worker
],
);
// Calculate expected frozen changes
const expectedFrozenChanges = [
0, // Proxy
-taskPrice, // Requester
-schedulerStake, // Scheduler
0, // AppProvider
0, // DatasetProvider
0, // Worker
];
await iexecWrapper.checkFrozenChanges(accountsInitialFrozens, expectedFrozenChanges);
}
});
2 changes: 2 additions & 0 deletions test/byContract/IexecPoco/IexecPoco1.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,8 @@ describe('IexecPoco1', () => {
// - identity groups
// - pre-signatures
// - low orders volumes
// - test when the lowest volume is in one of the orders
// - test when the lowest volume in order < unconsumed volume
// - multiple matches of the same order

it('Should fail when categories are different', async () => {
Expand Down
28 changes: 16 additions & 12 deletions test/utils/IexecWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
IexecLibOrders_v5,
IexecMaintenanceDelegate__factory,
IexecPoco2__factory,
IexecPocoAccessors__factory,
IexecPocoBoostAccessors__factory,
RLC__factory,
WorkerpoolRegistry,
Expand Down Expand Up @@ -135,7 +136,7 @@ export class IexecWrapper {
this.accounts.anyone,
).m_workerStakeRatioPolicy()
).toNumber();
return (workerpoolPrice * workerStakeRatio) / 100;
return Math.floor((workerpoolPrice * workerStakeRatio) / 100);
}

/**
Expand All @@ -153,8 +154,8 @@ export class IexecWrapper {
}

/**
* Compute the amount of RLC tokens that the worker receives
* as a reward per task.
* Compute the amount of RLC tokens that are rewarded to workers when
* a task is finalized.
* @param dealId
* @param mode
* @returns
Expand All @@ -173,10 +174,9 @@ export class IexecWrapper {
this.proxyAddress,
ethers.provider,
).viewDeal(dealId);
// (workerpoolPrice * workerRatio) / 100
return (
(deal.workerpool.price.toNumber() * (100 - deal.schedulerRewardRatio.toNumber())) / 100
);
// reward = (workerpoolPrice * workersRatio) / 100
const workersRewardRatio = 100 - deal.schedulerRewardRatio.toNumber();
return Math.floor((deal.workerpool.price.toNumber() * workersRewardRatio) / 100);
}

async setTeeBroker(brokerAddress: string) {
Expand Down Expand Up @@ -256,14 +256,18 @@ export class IexecWrapper {
const workerpoolOrder = orders.workerpool;
const requestOrder = orders.requester;
const taskIndex = (
await IexecAccessors__factory.connect(
this.proxyAddress,
this.accounts.anyone,
).viewConsumed(this.hashOrder(requestOrder))
await IexecAccessors__factory.connect(this.proxyAddress, ethers.provider).viewConsumed(
this.hashOrder(requestOrder),
)
).toNumber();
const dealId = getDealId(this.domain, requestOrder, taskIndex);
const taskId = getTaskId(dealId, taskIndex);
const volume = Number(requestOrder.volume);
const volume = (
await IexecPocoAccessors__factory.connect(
this.proxyAddress,
ethers.provider,
).computeDealVolume(appOrder, datasetOrder, workerpoolOrder, requestOrder)
).toNumber();
const taskPrice =
Number(appOrder.appprice) +
Number(datasetOrder.datasetprice) +
Expand Down
Loading
Loading