From 82bfa22f1935ccc547d1ff34e6f404f5cfd104d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= <33313130+jeremyjams@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:30:09 +0200 Subject: [PATCH 1/6] Migrate IexecOrderManagement unit tests --- CHANGELOG.md | 1 + .../IexecOrderManagement.ts | 393 ++++++++++++++++++ .../{close.js => close.js.skip} | 2 + .../{invalid.js => invalid.js.skip} | 2 + .../{sign.js => sign.js.skip} | 2 + test/utils/IexecWrapper.ts | 46 +- utils/createOrders.ts | 36 +- utils/poco-tools.ts | 5 + 8 files changed, 475 insertions(+), 12 deletions(-) create mode 100644 test/byContract/IexecOrderManagement/IexecOrderManagement.ts rename test/byContract/IexecOrderManagement/{close.js => close.js.skip} (99%) rename test/byContract/IexecOrderManagement/{invalid.js => invalid.js.skip} (99%) rename test/byContract/IexecOrderManagement/{sign.js => sign.js.skip} (99%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b701a073..499cd1a95 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## vNEXT - Migrate unit test files to Typescript & Hardhat: + - IexecOrderManagement (#101) - IexecMaintenance (#100) - IexecEscrowNative (#99) - IexecERC20 (#98) diff --git a/test/byContract/IexecOrderManagement/IexecOrderManagement.ts b/test/byContract/IexecOrderManagement/IexecOrderManagement.ts new file mode 100644 index 000000000..cc5e20bb8 --- /dev/null +++ b/test/byContract/IexecOrderManagement/IexecOrderManagement.ts @@ -0,0 +1,393 @@ +// SPDX-FileCopyrightText: 2020-2024 IEXEC BLOCKCHAIN TECH +// 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, + IexecLibOrders_v5, +} from '../../../typechain'; +import { NULL } from '../../../utils/constants'; +import { buildOrders, createOrderOperation } from '../../../utils/createOrders'; +import { OrderOperationEnum, getIexecAccounts } from '../../../utils/poco-tools'; +import { IexecWrapper } from '../../utils/IexecWrapper'; + +const volume = 3; +const someSignature = ethers.utils.hexZeroPad('0x1', 65); // non empty signature + +describe('OrderManagement', async () => { + let proxyAddress: string; + let [ + iexecPoco, + iexecPocoAsAppProvider, + iexecPocoAsDatasetProvider, + iexecPocoAsScheduler, + iexecPocoAsRequester, + ]: IexecInterfaceNative[] = []; + let iexecWrapper: IexecWrapper; + let [appAddress, datasetAddress, workerpoolAddress]: string[] = []; + let [anyone, appProvider, datasetProvider, scheduler, requester]: SignerWithAddress[] = []; + let appOrder: IexecLibOrders_v5.AppOrderStruct; + let datasetOrder: IexecLibOrders_v5.DatasetOrderStruct; + let workerpoolOrder: IexecLibOrders_v5.WorkerpoolOrderStruct; + let requestOrder: IexecLibOrders_v5.RequestOrderStruct; + let [appOrderHash, datasetOrderHash, workerpoolOrderHash, requestOrderHash]: string[] = []; + + async function deployContracts() { + proxyAddress = await loadHardhatFixtureDeployment(); + await loadFixture(initFixture); + } + + async function initFixture() { + const accounts = await getIexecAccounts(); + ({ appProvider, datasetProvider, scheduler, requester, anyone } = accounts); + iexecWrapper = new IexecWrapper(proxyAddress, accounts); + ({ appAddress, datasetAddress, workerpoolAddress } = await iexecWrapper.createAssets()); + iexecPoco = IexecInterfaceNative__factory.connect(proxyAddress, anyone); + iexecPocoAsAppProvider = iexecPoco.connect(appProvider); + iexecPocoAsDatasetProvider = iexecPoco.connect(datasetProvider); + iexecPocoAsScheduler = iexecPoco.connect(scheduler); + iexecPocoAsRequester = iexecPoco.connect(requester); + const appPrice = 1000; + const datasetPrice = 1_000_000; + const workerpoolPrice = 1_000_000_000; + const ordersAssets = { + app: appAddress, + dataset: datasetAddress, + workerpool: workerpoolAddress, + }; + const ordersPrices = { + app: appPrice, + dataset: datasetPrice, + workerpool: workerpoolPrice, + }; + ({ appOrder, datasetOrder, workerpoolOrder, requestOrder } = buildOrders({ + assets: ordersAssets, + requester: requester.address, + prices: ordersPrices, + volume, + })); + appOrderHash = iexecWrapper.hashOrder(appOrder); + datasetOrderHash = iexecWrapper.hashOrder(datasetOrder); + workerpoolOrderHash = iexecWrapper.hashOrder(workerpoolOrder); + requestOrderHash = iexecWrapper.hashOrder(requestOrder); + } + + describe('Manage orders when presign operations sent by owners', () => { + before(async () => { + await deployContracts(); + }); + + it('Should manage app order when presign operation sent by app provider', async () => { + await expect( + iexecPocoAsAppProvider.manageAppOrder( + createOrderOperation(appOrder, OrderOperationEnum.SIGN), + ), + ) + .to.emit(iexecPoco, 'SignedAppOrder') + .withArgs(appOrderHash); + expect(await iexecPoco.viewPresigned(appOrderHash)).equal(appProvider.address); + expect(await iexecPoco.verifyPresignature(appProvider.address, appOrderHash)).is.true; + expect( + await iexecPoco.verifyPresignatureOrSignature( + appProvider.address, + appOrderHash, + NULL.SIGNATURE, + ), + ).is.true; + }); + it('Should manage dataset order when presign operation sent by dataset provider', async () => { + await expect( + iexecPocoAsDatasetProvider.manageDatasetOrder( + createOrderOperation(datasetOrder, OrderOperationEnum.SIGN), + ), + ) + .to.emit(iexecPoco, 'SignedDatasetOrder') + .withArgs(datasetOrderHash); + expect(await iexecPoco.viewPresigned(datasetOrderHash)).equal(datasetProvider.address); + expect(await iexecPoco.verifyPresignature(datasetProvider.address, datasetOrderHash)).is + .true; + expect( + await iexecPoco.verifyPresignatureOrSignature( + datasetProvider.address, + datasetOrderHash, + NULL.SIGNATURE, + ), + ).is.true; + }); + it('Should manage workerpool order when presign operation sent by scheduler', async () => { + await expect( + iexecPocoAsScheduler.manageWorkerpoolOrder( + createOrderOperation(workerpoolOrder, OrderOperationEnum.SIGN), + ), + ) + .to.emit(iexecPoco, 'SignedWorkerpoolOrder') + .withArgs(workerpoolOrderHash); + expect(await iexecPoco.viewPresigned(workerpoolOrderHash)).equal(scheduler.address); + expect(await iexecPoco.verifyPresignature(scheduler.address, workerpoolOrderHash)).is + .true; + expect( + await iexecPoco.verifyPresignatureOrSignature( + scheduler.address, + workerpoolOrderHash, + NULL.SIGNATURE, + ), + ).is.true; + }); + it('Should manage request order when presign operation sent by requester', async () => { + await expect( + iexecPocoAsRequester.manageRequestOrder( + createOrderOperation(requestOrder, OrderOperationEnum.SIGN), + ), + ) + .to.emit(iexecPoco, 'SignedRequestOrder') + .withArgs(requestOrderHash); + expect(await iexecPoco.viewPresigned(requestOrderHash)).equal(requester.address); + expect(await iexecPoco.verifyPresignature(requester.address, requestOrderHash)).is.true; + expect( + await iexecPoco.verifyPresignatureOrSignature( + requester.address, + requestOrderHash, + NULL.SIGNATURE, + ), + ).is.true; + }); + it('Should match orders when presign operations sent by owners', async () => { + await depositInIexecAccounts(); + await expect( + iexecPoco.matchOrders(appOrder, datasetOrder, workerpoolOrder, requestOrder), + ).to.emit(iexecPoco, 'OrdersMatched'); + }); + }); + + describe('Manage orders when presign operations signed by owners', () => { + before(async () => { + await deployContracts(); + }); + + it('Should manage app order when presign operation signed by app provider', async () => { + const orderOperation = createOrderOperation(appOrder, OrderOperationEnum.SIGN); + await iexecWrapper.signOrderOperation(orderOperation, appProvider); + + await expect(iexecPoco.manageAppOrder(orderOperation)) + .to.emit(iexecPoco, 'SignedAppOrder') + .withArgs(appOrderHash); + expect(await iexecPoco.viewPresigned(appOrderHash)).equal(appProvider.address); + expect(await iexecPoco.verifyPresignature(appProvider.address, appOrderHash)).is.true; + expect( + await iexecPoco.verifyPresignatureOrSignature( + appProvider.address, + appOrderHash, + NULL.SIGNATURE, + ), + ).is.true; + }); + it('Should manage dataset order when presign operation signed by dataset provider', async () => { + const orderOperation = createOrderOperation(datasetOrder, OrderOperationEnum.SIGN); + await iexecWrapper.signOrderOperation(orderOperation, datasetProvider); + + await expect(iexecPoco.manageDatasetOrder(orderOperation)) + .to.emit(iexecPoco, 'SignedDatasetOrder') + .withArgs(datasetOrderHash); + expect(await iexecPoco.viewPresigned(datasetOrderHash)).equal(datasetProvider.address); + expect(await iexecPoco.verifyPresignature(datasetProvider.address, datasetOrderHash)).is + .true; + expect( + await iexecPoco.verifyPresignatureOrSignature( + datasetProvider.address, + datasetOrderHash, + NULL.SIGNATURE, + ), + ).is.true; + }); + it('Should manage workerpool order when presign operation signed by workerpool provider', async () => { + const orderOperation = createOrderOperation(workerpoolOrder, OrderOperationEnum.SIGN); + await iexecWrapper.signOrderOperation(orderOperation, scheduler); + + await expect(iexecPoco.manageWorkerpoolOrder(orderOperation)) + .to.emit(iexecPoco, 'SignedWorkerpoolOrder') + .withArgs(workerpoolOrderHash); + expect(await iexecPoco.viewPresigned(workerpoolOrderHash)).equal(scheduler.address); + expect(await iexecPoco.verifyPresignature(scheduler.address, workerpoolOrderHash)).is + .true; + expect( + await iexecPoco.verifyPresignatureOrSignature( + scheduler.address, + workerpoolOrderHash, + NULL.SIGNATURE, + ), + ).is.true; + }); + it('Should manage request order when presign operation signed by requester', async () => { + const orderOperation = createOrderOperation(requestOrder, OrderOperationEnum.SIGN); + await iexecWrapper.signOrderOperation(orderOperation, requester); + + await expect(iexecPoco.manageRequestOrder(orderOperation)) + .to.emit(iexecPoco, 'SignedRequestOrder') + .withArgs(requestOrderHash); + expect(await iexecPoco.viewPresigned(requestOrderHash)).equal(requester.address); + expect(await iexecPoco.verifyPresignature(requester.address, requestOrderHash)).is.true; + expect( + await iexecPoco.verifyPresignatureOrSignature( + requester.address, + requestOrderHash, + NULL.SIGNATURE, + ), + ).is.true; + }); + it('Should match orders when presign operations signed by owners', async () => { + await depositInIexecAccounts(); + await expect( + iexecPoco.matchOrders(appOrder, datasetOrder, workerpoolOrder, requestOrder), + ).to.emit(iexecPoco, 'OrdersMatched'); + }); + }); + + describe('Manage orders when close operations sent by owners', () => { + before(async () => { + await deployContracts(); + }); + + it('Should manage app order when close operation sent by app provider', async () => { + await expect( + iexecPocoAsAppProvider.manageAppOrder( + createOrderOperation(appOrder, OrderOperationEnum.CLOSE), + ), + ) + .to.emit(iexecPoco, 'ClosedAppOrder') + .withArgs(appOrderHash); + expect(await iexecPoco.viewConsumed(appOrderHash)).equal(volume); + }); + it('Should manage dataset order when close operation sent by dataset provider', async () => { + await expect( + iexecPocoAsDatasetProvider.manageDatasetOrder( + createOrderOperation(datasetOrder, OrderOperationEnum.CLOSE), + ), + ) + .to.emit(iexecPoco, 'ClosedDatasetOrder') + .withArgs(datasetOrderHash); + expect(await iexecPoco.viewConsumed(datasetOrderHash)).equal(volume); + }); + it('Should manage workerpool order when close operation sent by scheduler', async () => { + await expect( + iexecPocoAsScheduler.manageWorkerpoolOrder( + createOrderOperation(workerpoolOrder, OrderOperationEnum.CLOSE), + ), + ) + .to.emit(iexecPoco, 'ClosedWorkerpoolOrder') + .withArgs(workerpoolOrderHash); + expect(await iexecPoco.viewConsumed(workerpoolOrderHash)).equal(volume); + }); + it('Should manage request order when close operation sent by requester', async () => { + await expect( + iexecPocoAsRequester.manageRequestOrder( + createOrderOperation(requestOrder, OrderOperationEnum.CLOSE), + ), + ) + .to.emit(iexecPoco, 'ClosedRequestOrder') + .withArgs(requestOrderHash); + expect(await iexecPoco.viewConsumed(requestOrderHash)).equal(volume); + }); + }); + + describe('Manage orders when close operations signed by owners', () => { + before(async () => { + await deployContracts(); + }); + + it('Should manage app order when close operation signed by app provider', async () => { + const orderOperation = createOrderOperation(appOrder, OrderOperationEnum.CLOSE); + await iexecWrapper.signOrderOperation(orderOperation, appProvider); + + await expect(iexecPoco.manageAppOrder(orderOperation)) + .to.emit(iexecPoco, 'ClosedAppOrder') + .withArgs(appOrderHash); + expect(await iexecPoco.viewConsumed(appOrderHash)).equal(volume); + }); + it('Should manage dataset order when close operation signed by dataset provider', async () => { + const orderOperation = createOrderOperation(datasetOrder, OrderOperationEnum.CLOSE); + await iexecWrapper.signOrderOperation(orderOperation, datasetProvider); + + await expect(iexecPoco.manageDatasetOrder(orderOperation)) + .to.emit(iexecPoco, 'ClosedDatasetOrder') + .withArgs(datasetOrderHash); + expect(await iexecPoco.viewConsumed(datasetOrderHash)).equal(volume); + }); + it('Should manage workerpool order when close operation signed by scheduler', async () => { + const orderOperation = createOrderOperation(workerpoolOrder, OrderOperationEnum.CLOSE); + await iexecWrapper.signOrderOperation(orderOperation, scheduler); + + await expect(iexecPoco.manageWorkerpoolOrder(orderOperation)) + .to.emit(iexecPoco, 'ClosedWorkerpoolOrder') + .withArgs(workerpoolOrderHash); + expect(await iexecPoco.viewConsumed(workerpoolOrderHash)).equal(volume); + }); + it('Should manage request order when close operation signed by requester', async () => { + const orderOperation = createOrderOperation(requestOrder, OrderOperationEnum.CLOSE); + await iexecWrapper.signOrderOperation(orderOperation, requester); + + await expect(iexecPoco.manageRequestOrder(orderOperation)) + .to.emit(iexecPoco, 'ClosedRequestOrder') + .withArgs(requestOrderHash); + expect(await iexecPoco.viewConsumed(requestOrderHash)).equal(volume); + }); + }); + + describe('Should not manage orders when invalid sender or signature', () => { + before(async () => { + await deployContracts(); + }); + + it('Should not manage app order when invalid sender or signature', async () => { + await expect( + iexecPoco.manageAppOrder({ + order: appOrder, + operation: OrderOperationEnum.SIGN, // any is fine + sign: someSignature, + }), + ).to.be.revertedWith('invalid-sender-or-signature'); + }); + it('Should not manage dataset order when invalid sender or signature', async () => { + await expect( + iexecPoco.manageDatasetOrder({ + order: datasetOrder, + operation: OrderOperationEnum.SIGN, // any is fine + sign: someSignature, + }), + ).to.be.revertedWith('invalid-sender-or-signature'); + }); + it('Should not manage workerpool order when invalid sender or signature', async () => { + await expect( + iexecPoco.manageWorkerpoolOrder({ + order: workerpoolOrder, + operation: OrderOperationEnum.SIGN, // any is fine + sign: someSignature, + }), + ).to.be.revertedWith('invalid-sender-or-signature'); + }); + it('Should not manage request order when invalid sender or signature', async () => { + await expect( + iexecPoco.manageRequestOrder({ + order: requestOrder, + operation: OrderOperationEnum.SIGN, // any is fine + sign: someSignature, + }), + ).to.be.revertedWith('invalid-sender-or-signature'); + }); + }); + + async function depositInIexecAccounts() { + const taskPrice = + Number(appOrder.appprice) + + Number(datasetOrder.datasetprice) + + Number(workerpoolOrder.workerpoolprice); + const dealPrice = taskPrice * volume; + await iexecWrapper.depositInIexecAccount(requester, dealPrice); + await iexecWrapper + .computeSchedulerDealStake(Number(workerpoolOrder.workerpoolprice), volume) + .then((stake) => iexecWrapper.depositInIexecAccount(scheduler, stake)); + } +}); diff --git a/test/byContract/IexecOrderManagement/close.js b/test/byContract/IexecOrderManagement/close.js.skip similarity index 99% rename from test/byContract/IexecOrderManagement/close.js rename to test/byContract/IexecOrderManagement/close.js.skip index 63f7e47d9..d6b223ddb 100644 --- a/test/byContract/IexecOrderManagement/close.js +++ b/test/byContract/IexecOrderManagement/close.js.skip @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: 2020-2024 IEXEC BLOCKCHAIN TECH // SPDX-License-Identifier: Apache-2.0 +// TODO: Remove this file replaced by IexecOrderManagement.ts + const loadTruffleFixtureDeployment = require('../../../scripts/truffle-fixture-deployer'); // Config var DEPLOYMENT = require('../../../config/config.json').chains.default; diff --git a/test/byContract/IexecOrderManagement/invalid.js b/test/byContract/IexecOrderManagement/invalid.js.skip similarity index 99% rename from test/byContract/IexecOrderManagement/invalid.js rename to test/byContract/IexecOrderManagement/invalid.js.skip index 33d886529..53e5eaf47 100644 --- a/test/byContract/IexecOrderManagement/invalid.js +++ b/test/byContract/IexecOrderManagement/invalid.js.skip @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: 2020-2024 IEXEC BLOCKCHAIN TECH // SPDX-License-Identifier: Apache-2.0 +// TODO: Remove this file replaced by IexecOrderManagement.ts + const loadTruffleFixtureDeployment = require('../../../scripts/truffle-fixture-deployer'); // Config var DEPLOYMENT = require('../../../config/config.json').chains.default; diff --git a/test/byContract/IexecOrderManagement/sign.js b/test/byContract/IexecOrderManagement/sign.js.skip similarity index 99% rename from test/byContract/IexecOrderManagement/sign.js rename to test/byContract/IexecOrderManagement/sign.js.skip index 86149411b..445a81075 100644 --- a/test/byContract/IexecOrderManagement/sign.js +++ b/test/byContract/IexecOrderManagement/sign.js.skip @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: 2020-2024 IEXEC BLOCKCHAIN TECH // SPDX-License-Identifier: Apache-2.0 +// TODO: Remove this file replaced by IexecOrderManagement.ts + const loadTruffleFixtureDeployment = require('../../../scripts/truffle-fixture-deployer'); // Config var DEPLOYMENT = require('../../../config/config.json').chains.default; diff --git a/test/utils/IexecWrapper.ts b/test/utils/IexecWrapper.ts index 4518c0e63..5c4aac23d 100644 --- a/test/utils/IexecWrapper.ts +++ b/test/utils/IexecWrapper.ts @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: 2024 IEXEC BLOCKCHAIN TECH // SPDX-License-Identifier: Apache-2.0 +import { TypedDataDomain } from '@ethersproject/abstract-signer'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; import { BigNumber, ContractReceipt } from 'ethers'; import hre, { ethers } from 'hardhat'; @@ -18,7 +19,14 @@ import { WorkerpoolRegistry__factory, } from '../../typechain'; import { IexecPoco1__factory } from '../../typechain/factories/contracts/modules/interfaces/IexecPoco1.v8.sol'; -import { IexecOrders, Orders, hashOrder, signOrders } from '../../utils/createOrders'; +import { + IexecOrders, + OrderOperation, + Orders, + hashOrder, + signOrderOperation, + signOrders, +} from '../../utils/createOrders'; import { IexecAccounts, getDealId, getTaskId, setNextBlockTimestamp } from '../../utils/poco-tools'; import { extractEventsFromReceipt } from '../../utils/tools'; const DEPLOYMENT_CONFIG = config.chains.default; @@ -26,10 +34,17 @@ const DEPLOYMENT_CONFIG = config.chains.default; export class IexecWrapper { proxyAddress: string; accounts: IexecAccounts; + domain: TypedDataDomain; constructor(proxyAddress: string, accounts: IexecAccounts) { this.proxyAddress = proxyAddress; this.accounts = accounts; + this.domain = { + name: 'iExecODB', + version: '5.0.0', + chainId: hre.network.config.chainId, + verifyingContract: this.proxyAddress, + }; } /** @@ -89,6 +104,23 @@ export class IexecWrapper { .then((tx) => tx.wait()); } + /** + * Hash an order using current domain. + */ + hashOrder(order: Record) { + return hashOrder(this.domain, order); + } + + /** + * Sign an order operation using current domain. + */ + async signOrderOperation( + orderOperation: OrderOperation, + signer: SignerWithAddress, + ): Promise { + return signOrderOperation(this.domain, orderOperation, signer); + } + async signAndSponsorMatchOrders(orders: IexecOrders) { return this._signAndMatchOrders(orders, true); } @@ -104,13 +136,7 @@ export class IexecWrapper { * Otherwise the requester will be in charge of paying for the deal. */ private async _signAndMatchOrders(orders: IexecOrders, withSponsor: boolean) { - const domain = { - name: 'iExecODB', - version: '5.0.0', - chainId: hre.network.config.chainId, - verifyingContract: this.proxyAddress, - }; - await signOrders(domain, orders, { + await signOrders(this.domain, orders, { appOwner: this.accounts.appProvider, datasetOwner: this.accounts.datasetProvider, workerpoolOwner: this.accounts.scheduler, @@ -124,9 +150,9 @@ export class IexecWrapper { await IexecAccessors__factory.connect( this.proxyAddress, this.accounts.anyone, - ).viewConsumed(hashOrder(domain, requestOrder)) + ).viewConsumed(hashOrder(this.domain, requestOrder)) ).toNumber(); - const dealId = getDealId(domain, requestOrder, taskIndex); + const dealId = getDealId(this.domain, requestOrder, taskIndex); const taskId = getTaskId(dealId, taskIndex); const volume = Number(requestOrder.volume); const taskPrice = diff --git a/utils/createOrders.ts b/utils/createOrders.ts index fb3b5df56..3c623596a 100644 --- a/utils/createOrders.ts +++ b/utils/createOrders.ts @@ -1,12 +1,14 @@ -// SPDX-FileCopyrightText: 2023 IEXEC BLOCKCHAIN TECH +// SPDX-FileCopyrightText: 2023-2024 IEXEC BLOCKCHAIN TECH // SPDX-License-Identifier: Apache-2.0 import { TypedDataDomain } from '@ethersproject/abstract-signer'; +import { BigNumber } from '@ethersproject/bignumber'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; import { ethers } from 'hardhat'; import { IexecLibOrders_v5 } from '../typechain'; -import constants from './constants'; +import constants, { NULL } from './constants'; import { utils } from './odb-tools'; +import { OrderOperationEnum } from './poco-tools'; export type Orders = [ IexecLibOrders_v5.AppOrderStruct, @@ -52,6 +54,12 @@ export interface IexecOrders { requester: IexecLibOrders_v5.RequestOrderStruct; } +export interface OrderOperation { + order: Record; + operation: BigNumber; + sign: string; +} + export function createEmptyAppOrder(): IexecLibOrders_v5.AppOrderStruct { return { app: constants.NULL.ADDRESS, @@ -117,6 +125,13 @@ export function createEmptyDatasetOrder(): IexecLibOrders_v5.DatasetOrderStruct }; } +/** + * Create an order operation from an existing order. + */ +export function createOrderOperation(order: OrderType, operation: OrderOperationEnum) { + return { order, operation: BigNumber.from(operation), sign: NULL.SIGNATURE }; +} + export function buildOrders(matchOrdersArgs: MatchOrdersArgs) { let requestOrder = createEmptyRequestOrder(); let appOrder = createEmptyAppOrder(); @@ -240,6 +255,23 @@ export async function signOrder( return utils.signStruct(getTypeOf(order), order, domain, signer); } +/** + * Sign an iExec EIP712 order operation for app, dataset, workerpool or request + * order operations. + */ +export async function signOrderOperation( + domain: TypedDataDomain, + orderOperation: OrderOperation, + signer: SignerWithAddress, +): Promise { + return utils.signStruct( + getTypeOf(orderOperation.order) + 'Operation', + orderOperation, + domain, + signer, + ); +} + /** * Get typed data hash of order: app, dataset, workerpool or request * @returns order hash diff --git a/utils/poco-tools.ts b/utils/poco-tools.ts index c160081d7..0788c83af 100644 --- a/utils/poco-tools.ts +++ b/utils/poco-tools.ts @@ -22,6 +22,11 @@ export enum TaskStatusEnum { FAILED, } +export enum OrderOperationEnum { + SIGN, + CLOSE, +} + export interface IexecAccounts { iexecAdmin: SignerWithAddress; requester: SignerWithAddress; From 120b661e5b0e5402eefc6f7afadf33b0d380148d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= <33313130+jeremyjams@users.noreply.github.com> Date: Tue, 6 Aug 2024 16:44:41 +0200 Subject: [PATCH 2/6] Use hashOrder from wrapper class --- test/utils/IexecWrapper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/utils/IexecWrapper.ts b/test/utils/IexecWrapper.ts index 5c4aac23d..0f07278f0 100644 --- a/test/utils/IexecWrapper.ts +++ b/test/utils/IexecWrapper.ts @@ -150,7 +150,7 @@ export class IexecWrapper { await IexecAccessors__factory.connect( this.proxyAddress, this.accounts.anyone, - ).viewConsumed(hashOrder(this.domain, requestOrder)) + ).viewConsumed(this.hashOrder(requestOrder)) ).toNumber(); const dealId = getDealId(this.domain, requestOrder, taskIndex); const taskId = getTaskId(dealId, taskIndex); From 895e115cb0605d9bc4202a6edecfed5a9eb1c2e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= <33313130+jeremyjams@users.noreply.github.com> Date: Wed, 7 Aug 2024 09:42:12 +0200 Subject: [PATCH 3/6] Use local variables for asset addresses --- test/byContract/IexecOrderManagement/IexecOrderManagement.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/byContract/IexecOrderManagement/IexecOrderManagement.ts b/test/byContract/IexecOrderManagement/IexecOrderManagement.ts index cc5e20bb8..86a8ac09f 100644 --- a/test/byContract/IexecOrderManagement/IexecOrderManagement.ts +++ b/test/byContract/IexecOrderManagement/IexecOrderManagement.ts @@ -28,7 +28,6 @@ describe('OrderManagement', async () => { iexecPocoAsRequester, ]: IexecInterfaceNative[] = []; let iexecWrapper: IexecWrapper; - let [appAddress, datasetAddress, workerpoolAddress]: string[] = []; let [anyone, appProvider, datasetProvider, scheduler, requester]: SignerWithAddress[] = []; let appOrder: IexecLibOrders_v5.AppOrderStruct; let datasetOrder: IexecLibOrders_v5.DatasetOrderStruct; @@ -45,7 +44,7 @@ describe('OrderManagement', async () => { const accounts = await getIexecAccounts(); ({ appProvider, datasetProvider, scheduler, requester, anyone } = accounts); iexecWrapper = new IexecWrapper(proxyAddress, accounts); - ({ appAddress, datasetAddress, workerpoolAddress } = await iexecWrapper.createAssets()); + const { appAddress, datasetAddress, workerpoolAddress } = await iexecWrapper.createAssets(); iexecPoco = IexecInterfaceNative__factory.connect(proxyAddress, anyone); iexecPocoAsAppProvider = iexecPoco.connect(appProvider); iexecPocoAsDatasetProvider = iexecPoco.connect(datasetProvider); From 81f079d12c06abfafca123c0923ad96fda573e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= <33313130+jeremyjams@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:35:15 +0200 Subject: [PATCH 4/6] No matchOrders in OrderManagement tests --- .../IexecOrderManagement.ts | 48 +------------------ 1 file changed, 2 insertions(+), 46 deletions(-) diff --git a/test/byContract/IexecOrderManagement/IexecOrderManagement.ts b/test/byContract/IexecOrderManagement/IexecOrderManagement.ts index 86a8ac09f..4f759525a 100644 --- a/test/byContract/IexecOrderManagement/IexecOrderManagement.ts +++ b/test/byContract/IexecOrderManagement/IexecOrderManagement.ts @@ -35,10 +35,10 @@ describe('OrderManagement', async () => { let requestOrder: IexecLibOrders_v5.RequestOrderStruct; let [appOrderHash, datasetOrderHash, workerpoolOrderHash, requestOrderHash]: string[] = []; - async function deployContracts() { + before(async () => { proxyAddress = await loadHardhatFixtureDeployment(); await loadFixture(initFixture); - } + }); async function initFixture() { const accounts = await getIexecAccounts(); @@ -76,10 +76,6 @@ describe('OrderManagement', async () => { } describe('Manage orders when presign operations sent by owners', () => { - before(async () => { - await deployContracts(); - }); - it('Should manage app order when presign operation sent by app provider', async () => { await expect( iexecPocoAsAppProvider.manageAppOrder( @@ -154,19 +150,9 @@ describe('OrderManagement', async () => { ), ).is.true; }); - it('Should match orders when presign operations sent by owners', async () => { - await depositInIexecAccounts(); - await expect( - iexecPoco.matchOrders(appOrder, datasetOrder, workerpoolOrder, requestOrder), - ).to.emit(iexecPoco, 'OrdersMatched'); - }); }); describe('Manage orders when presign operations signed by owners', () => { - before(async () => { - await deployContracts(); - }); - it('Should manage app order when presign operation signed by app provider', async () => { const orderOperation = createOrderOperation(appOrder, OrderOperationEnum.SIGN); await iexecWrapper.signOrderOperation(orderOperation, appProvider); @@ -237,19 +223,9 @@ describe('OrderManagement', async () => { ), ).is.true; }); - it('Should match orders when presign operations signed by owners', async () => { - await depositInIexecAccounts(); - await expect( - iexecPoco.matchOrders(appOrder, datasetOrder, workerpoolOrder, requestOrder), - ).to.emit(iexecPoco, 'OrdersMatched'); - }); }); describe('Manage orders when close operations sent by owners', () => { - before(async () => { - await deployContracts(); - }); - it('Should manage app order when close operation sent by app provider', async () => { await expect( iexecPocoAsAppProvider.manageAppOrder( @@ -293,10 +269,6 @@ describe('OrderManagement', async () => { }); describe('Manage orders when close operations signed by owners', () => { - before(async () => { - await deployContracts(); - }); - it('Should manage app order when close operation signed by app provider', async () => { const orderOperation = createOrderOperation(appOrder, OrderOperationEnum.CLOSE); await iexecWrapper.signOrderOperation(orderOperation, appProvider); @@ -336,10 +308,6 @@ describe('OrderManagement', async () => { }); describe('Should not manage orders when invalid sender or signature', () => { - before(async () => { - await deployContracts(); - }); - it('Should not manage app order when invalid sender or signature', async () => { await expect( iexecPoco.manageAppOrder({ @@ -377,16 +345,4 @@ describe('OrderManagement', async () => { ).to.be.revertedWith('invalid-sender-or-signature'); }); }); - - async function depositInIexecAccounts() { - const taskPrice = - Number(appOrder.appprice) + - Number(datasetOrder.datasetprice) + - Number(workerpoolOrder.workerpoolprice); - const dealPrice = taskPrice * volume; - await iexecWrapper.depositInIexecAccount(requester, dealPrice); - await iexecWrapper - .computeSchedulerDealStake(Number(workerpoolOrder.workerpoolprice), volume) - .then((stake) => iexecWrapper.depositInIexecAccount(scheduler, stake)); - } }); From 70080b8f47c5ed66728d7c881aecba06a97c57e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= <33313130+jeremyjams@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:38:17 +0200 Subject: [PATCH 5/6] Rename tests --- .../IexecOrderManagement.ts | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/test/byContract/IexecOrderManagement/IexecOrderManagement.ts b/test/byContract/IexecOrderManagement/IexecOrderManagement.ts index 4f759525a..626dd4c05 100644 --- a/test/byContract/IexecOrderManagement/IexecOrderManagement.ts +++ b/test/byContract/IexecOrderManagement/IexecOrderManagement.ts @@ -75,8 +75,8 @@ describe('OrderManagement', async () => { requestOrderHash = iexecWrapper.hashOrder(requestOrder); } - describe('Manage orders when presign operations sent by owners', () => { - it('Should manage app order when presign operation sent by app provider', async () => { + describe('Presign orders when operations are sent by owners', () => { + it('Should presign app order when operation is sent by app provider', async () => { await expect( iexecPocoAsAppProvider.manageAppOrder( createOrderOperation(appOrder, OrderOperationEnum.SIGN), @@ -94,7 +94,7 @@ describe('OrderManagement', async () => { ), ).is.true; }); - it('Should manage dataset order when presign operation sent by dataset provider', async () => { + it('Should presign dataset order when operation is sent by dataset provider', async () => { await expect( iexecPocoAsDatasetProvider.manageDatasetOrder( createOrderOperation(datasetOrder, OrderOperationEnum.SIGN), @@ -113,7 +113,7 @@ describe('OrderManagement', async () => { ), ).is.true; }); - it('Should manage workerpool order when presign operation sent by scheduler', async () => { + it('Should presign workerpool order when operation is sent by scheduler', async () => { await expect( iexecPocoAsScheduler.manageWorkerpoolOrder( createOrderOperation(workerpoolOrder, OrderOperationEnum.SIGN), @@ -132,7 +132,7 @@ describe('OrderManagement', async () => { ), ).is.true; }); - it('Should manage request order when presign operation sent by requester', async () => { + it('Should presign request order when operation is sent by requester', async () => { await expect( iexecPocoAsRequester.manageRequestOrder( createOrderOperation(requestOrder, OrderOperationEnum.SIGN), @@ -152,8 +152,8 @@ describe('OrderManagement', async () => { }); }); - describe('Manage orders when presign operations signed by owners', () => { - it('Should manage app order when presign operation signed by app provider', async () => { + describe('Presign orders when operations are signed by owners', () => { + it('Should presign app order when operation is signed by app provider', async () => { const orderOperation = createOrderOperation(appOrder, OrderOperationEnum.SIGN); await iexecWrapper.signOrderOperation(orderOperation, appProvider); @@ -170,7 +170,7 @@ describe('OrderManagement', async () => { ), ).is.true; }); - it('Should manage dataset order when presign operation signed by dataset provider', async () => { + it('Should presign dataset order when operation is signed by dataset provider', async () => { const orderOperation = createOrderOperation(datasetOrder, OrderOperationEnum.SIGN); await iexecWrapper.signOrderOperation(orderOperation, datasetProvider); @@ -188,7 +188,7 @@ describe('OrderManagement', async () => { ), ).is.true; }); - it('Should manage workerpool order when presign operation signed by workerpool provider', async () => { + it('Should presign workerpool order when operation is signed by workerpool provider', async () => { const orderOperation = createOrderOperation(workerpoolOrder, OrderOperationEnum.SIGN); await iexecWrapper.signOrderOperation(orderOperation, scheduler); @@ -206,7 +206,7 @@ describe('OrderManagement', async () => { ), ).is.true; }); - it('Should manage request order when presign operation signed by requester', async () => { + it('Should presign request order when operation is signed by requester', async () => { const orderOperation = createOrderOperation(requestOrder, OrderOperationEnum.SIGN); await iexecWrapper.signOrderOperation(orderOperation, requester); @@ -225,8 +225,8 @@ describe('OrderManagement', async () => { }); }); - describe('Manage orders when close operations sent by owners', () => { - it('Should manage app order when close operation sent by app provider', async () => { + describe('Close orders when operations are sent by owners', () => { + it('Should close app order when operation is sent by app provider', async () => { await expect( iexecPocoAsAppProvider.manageAppOrder( createOrderOperation(appOrder, OrderOperationEnum.CLOSE), @@ -236,7 +236,7 @@ describe('OrderManagement', async () => { .withArgs(appOrderHash); expect(await iexecPoco.viewConsumed(appOrderHash)).equal(volume); }); - it('Should manage dataset order when close operation sent by dataset provider', async () => { + it('Should close dataset order when operation is sent by dataset provider', async () => { await expect( iexecPocoAsDatasetProvider.manageDatasetOrder( createOrderOperation(datasetOrder, OrderOperationEnum.CLOSE), @@ -246,7 +246,7 @@ describe('OrderManagement', async () => { .withArgs(datasetOrderHash); expect(await iexecPoco.viewConsumed(datasetOrderHash)).equal(volume); }); - it('Should manage workerpool order when close operation sent by scheduler', async () => { + it('Should close workerpool order when operation is sent by scheduler', async () => { await expect( iexecPocoAsScheduler.manageWorkerpoolOrder( createOrderOperation(workerpoolOrder, OrderOperationEnum.CLOSE), @@ -256,7 +256,7 @@ describe('OrderManagement', async () => { .withArgs(workerpoolOrderHash); expect(await iexecPoco.viewConsumed(workerpoolOrderHash)).equal(volume); }); - it('Should manage request order when close operation sent by requester', async () => { + it('Should close request order when operation is sent by requester', async () => { await expect( iexecPocoAsRequester.manageRequestOrder( createOrderOperation(requestOrder, OrderOperationEnum.CLOSE), @@ -268,8 +268,8 @@ describe('OrderManagement', async () => { }); }); - describe('Manage orders when close operations signed by owners', () => { - it('Should manage app order when close operation signed by app provider', async () => { + describe('Close orders when operations are signed by owners', () => { + it('Should close app order when operation is signed by app provider', async () => { const orderOperation = createOrderOperation(appOrder, OrderOperationEnum.CLOSE); await iexecWrapper.signOrderOperation(orderOperation, appProvider); @@ -278,7 +278,7 @@ describe('OrderManagement', async () => { .withArgs(appOrderHash); expect(await iexecPoco.viewConsumed(appOrderHash)).equal(volume); }); - it('Should manage dataset order when close operation signed by dataset provider', async () => { + it('Should close dataset order when operation is signed by dataset provider', async () => { const orderOperation = createOrderOperation(datasetOrder, OrderOperationEnum.CLOSE); await iexecWrapper.signOrderOperation(orderOperation, datasetProvider); @@ -287,7 +287,7 @@ describe('OrderManagement', async () => { .withArgs(datasetOrderHash); expect(await iexecPoco.viewConsumed(datasetOrderHash)).equal(volume); }); - it('Should manage workerpool order when close operation signed by scheduler', async () => { + it('Should close workerpool order when operation is signed by scheduler', async () => { const orderOperation = createOrderOperation(workerpoolOrder, OrderOperationEnum.CLOSE); await iexecWrapper.signOrderOperation(orderOperation, scheduler); @@ -296,7 +296,7 @@ describe('OrderManagement', async () => { .withArgs(workerpoolOrderHash); expect(await iexecPoco.viewConsumed(workerpoolOrderHash)).equal(volume); }); - it('Should manage request order when close operation signed by requester', async () => { + it('Should close request order when operation is signed by requester', async () => { const orderOperation = createOrderOperation(requestOrder, OrderOperationEnum.CLOSE); await iexecWrapper.signOrderOperation(orderOperation, requester); From cf94763e4fd1b3cdd8ea11a5dbc708abaca7155a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= <33313130+jeremyjams@users.noreply.github.com> Date: Wed, 7 Aug 2024 16:39:59 +0200 Subject: [PATCH 6/6] Use before each Co-authored-by: Zied Guesmi <26070035+zguesmi@users.noreply.github.com> --- test/byContract/IexecOrderManagement/IexecOrderManagement.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/byContract/IexecOrderManagement/IexecOrderManagement.ts b/test/byContract/IexecOrderManagement/IexecOrderManagement.ts index 626dd4c05..a98cd2b07 100644 --- a/test/byContract/IexecOrderManagement/IexecOrderManagement.ts +++ b/test/byContract/IexecOrderManagement/IexecOrderManagement.ts @@ -35,7 +35,7 @@ describe('OrderManagement', async () => { let requestOrder: IexecLibOrders_v5.RequestOrderStruct; let [appOrderHash, datasetOrderHash, workerpoolOrderHash, requestOrderHash]: string[] = []; - before(async () => { + beforeEach(async () => { proxyAddress = await loadHardhatFixtureDeployment(); await loadFixture(initFixture); });