diff --git a/CHANGELOG.md b/CHANGELOG.md index add8f5d64..9131f0aa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) diff --git a/test/000_fullchain.js b/test/000_fullchain.js.skip similarity index 100% rename from test/000_fullchain.js rename to test/000_fullchain.js.skip diff --git a/test/000_fullchain.test.ts b/test/000_fullchain.test.ts index e187d1c87..fea503449 100644 --- a/test/000_fullchain.test.ts +++ b/test/000_fullchain.test.ts @@ -3,12 +3,13 @@ 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, @@ -16,41 +17,54 @@ import { 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(); @@ -61,7 +75,6 @@ describe('IexecPoco (IT)', function () { async function initFixture() { const accounts = await getIexecAccounts(); ({ - iexecAdmin, requester, sponsor, beneficiary, @@ -69,24 +82,17 @@ describe('IexecPoco (IT)', function () { 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, @@ -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({ @@ -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); + } +} diff --git a/test/byContract/IexecAccessors/IexecAccessors.test.ts b/test/byContract/IexecAccessors/IexecAccessors.test.ts index 61b59d3b6..57d28873a 100644 --- a/test/byContract/IexecAccessors/IexecAccessors.test.ts +++ b/test/byContract/IexecAccessors/IexecAccessors.test.ts @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2020-2024 IEXEC BLOCKCHAIN TECH // 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'; @@ -14,9 +14,7 @@ import { import { OrdersAssets, buildOrders } from '../../../utils/createOrders'; import { TaskStatusEnum, - buildAndSignContributionAuthorizationMessage, buildResultCallbackAndDigest, - buildResultHashAndResultSeal, buildUtf8ResultAndDigest, getIexecAccounts, getTaskId, @@ -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() @@ -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) @@ -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); @@ -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); diff --git a/test/byContract/IexecPoco/IexecPoco1.test.ts b/test/byContract/IexecPoco/IexecPoco1.test.ts index caebe25ee..394293edf 100644 --- a/test/byContract/IexecPoco/IexecPoco1.test.ts +++ b/test/byContract/IexecPoco/IexecPoco1.test.ts @@ -379,8 +379,8 @@ describe('IexecPoco1', () => { workerpoolAddress, workerpoolPrice, ); - const schedulerRewardByTask = - await iexecWrapper.getSchedulerTaskRewardRatio(workerpoolAddress); + const schedulerRewardRatioPerTask = + await iexecWrapper.getSchedulerRewardRatio(workerpoolAddress); // Deposit required amounts. await iexecWrapper.depositInIexecAccount(requester, dealPrice); await iexecWrapper.depositInIexecAccount(scheduler, schedulerStake); @@ -450,7 +450,7 @@ describe('IexecPoco1', () => { expect(deal.botFirst).to.equal(0); expect(deal.botSize).to.equal(botVolume); expect(deal.workerStake).to.equal(workerStakePerTask); - expect(deal.schedulerRewardRatio).to.equal(schedulerRewardByTask); + expect(deal.schedulerRewardRatio).to.equal(schedulerRewardRatioPerTask); expect(deal.sponsor).to.equal(requester.address); }); @@ -505,7 +505,7 @@ describe('IexecPoco1', () => { await iexecWrapper.computeWorkerTaskStake(workerpoolAddress, workerpoolPrice), ); expect(deal.schedulerRewardRatio).to.equal( - await iexecWrapper.getSchedulerTaskRewardRatio(workerpoolAddress), + await iexecWrapper.getSchedulerRewardRatio(workerpoolAddress), ); expect(deal.sponsor).to.equal(requester.address); }); diff --git a/test/utils/IexecWrapper.ts b/test/utils/IexecWrapper.ts index 19d7b0ce0..1db509b00 100644 --- a/test/utils/IexecWrapper.ts +++ b/test/utils/IexecWrapper.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { TypedDataDomain } from '@ethersproject/abstract-signer'; +import { AddressZero } from '@ethersproject/constants'; import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; import { BigNumber, ContractReceipt } from 'ethers'; import hre, { ethers } from 'hardhat'; @@ -15,6 +16,8 @@ import { IexecInterfaceNative__factory, IexecLibOrders_v5, IexecMaintenanceDelegate__factory, + IexecPoco2__factory, + IexecPocoBoostAccessors__factory, RLC__factory, WorkerpoolRegistry, WorkerpoolRegistry__factory, @@ -28,7 +31,15 @@ import { signOrderOperation, signOrders, } from '../../utils/createOrders'; -import { IexecAccounts, getDealId, getTaskId, setNextBlockTimestamp } from '../../utils/poco-tools'; +import { + IexecAccounts, + PocoMode, + buildAndSignContributionAuthorizationMessage, + buildResultHashAndResultSeal, + getDealId, + getTaskId, + setNextBlockTimestamp, +} from '../../utils/poco-tools'; import { extractEventsFromReceipt } from '../../utils/tools'; const DEPLOYMENT_CONFIG = config.chains.default; @@ -88,6 +99,7 @@ export class IexecWrapper { } } + // TODO rename to computeSchedulerStakePerDeal /** * Compute the amount of RLCs to be staked by the scheduler * for a deal. We first compute the percentage by task @@ -126,12 +138,11 @@ export class IexecWrapper { } /** - * Compute the amount of RLC tokens that the scheduler receives - * as a reward by task. + * Get the scheduler reward ratio policy. * @param workerpoolAddress address of the workerpool * @returns value of the reward */ - async getSchedulerTaskRewardRatio(workerpoolAddress: string) { + async getSchedulerRewardRatio(workerpoolAddress: string) { return ( await Workerpool__factory.connect( workerpoolAddress, @@ -140,6 +151,33 @@ export class IexecWrapper { ).toNumber(); } + /** + * Compute the amount of RLC tokens that the worker receives + * as a reward per task. + * @param dealId + * @param mode + * @returns + */ + async computeWorkerRewardPerTask(dealId: string, mode: PocoMode) { + if (mode === PocoMode.BOOST) { + return ( + await IexecPocoBoostAccessors__factory.connect( + this.proxyAddress, + ethers.provider, + ).viewDealBoost(dealId) + ).workerReward.toNumber(); + } + // CLASSIC mode. + const deal = await IexecAccessors__factory.connect( + this.proxyAddress, + ethers.provider, + ).viewDeal(dealId); + // (workerpoolPrice * workerRatio) / 100 + return ( + (deal.workerpool.price.toNumber() * (100 - deal.schedulerRewardRatio.toNumber())) / 100 + ); + } + async setTeeBroker(brokerAddress: string) { await IexecMaintenanceDelegate__factory.connect(this.proxyAddress, this.accounts.iexecAdmin) .setTeeBroker(brokerAddress) @@ -232,9 +270,11 @@ export class IexecWrapper { const dealPrice = taskPrice * volume; const dealPayer = withSponsor ? this.accounts.sponsor : this.accounts.requester; await this.depositInIexecAccount(dealPayer, dealPrice); - await this.computeSchedulerDealStake(Number(workerpoolOrder.workerpoolprice), volume).then( - (stake) => this.depositInIexecAccount(this.accounts.scheduler, stake), + const schedulerStakePerDeal = await this.computeSchedulerDealStake( + Number(workerpoolOrder.workerpoolprice), + volume, ); + await this.depositInIexecAccount(this.accounts.scheduler, schedulerStakePerDeal); const startTime = await setNextBlockTimestamp(); const iexecPocoAsDealPayer = IexecPoco1__factory.connect(this.proxyAddress, dealPayer); await ( @@ -242,7 +282,7 @@ export class IexecWrapper { ? iexecPocoAsDealPayer.sponsorMatchOrders(...orders.toArray()) : iexecPocoAsDealPayer.matchOrders(...orders.toArray()) ).then((tx) => tx.wait()); - return { dealId, taskId, taskIndex, dealPrice, startTime }; + return { dealId, taskId, taskIndex, dealPrice, schedulerStakePerDeal, startTime }; } async createAssets() { @@ -289,6 +329,69 @@ export class IexecWrapper { return await extractRegistryEntryAddress(datasetReceipt, datasetRegistry.address); } + /** + * Helper function to initialize a task. + * @param dealId id of the deal + * @param taskIndex index of the task + * @returns + */ + async initializeTask(dealId: string, taskIndex: number) { + await IexecPoco2__factory.connect(this.proxyAddress, this.accounts.anyone) + .initialize(dealId, taskIndex) + .then((tx) => tx.wait()); + return getTaskId(dealId, taskIndex); + } + + /** + * Helper function to contribute to a task. The contributor's stake is + * automatically deposited before contributing. + * Note: no enclave address is used. + * @param contributor Signer to sign the contribution + * @param dealId id of the deal + * @param taskIndex index of the task. + * @param resultDigest hash of the result + * @returns id of the task + */ + async contributeToTask( + dealId: string, + taskIndex: number, + resultDigest: string, + contributor: SignerWithAddress, + ) { + const enclaveAddress = AddressZero; + const enclaveSignature = '0x'; + const taskId = getTaskId(dealId, taskIndex); + const workerStakePerTask = await IexecAccessors__factory.connect( + this.proxyAddress, + ethers.provider, + ) + .viewDeal(dealId) + .then((deal) => deal.workerStake.toNumber()); + const { resultHash, resultSeal } = buildResultHashAndResultSeal( + taskId, + resultDigest, + contributor, + ); + const schedulerSignature = await buildAndSignContributionAuthorizationMessage( + contributor.address, + taskId, + enclaveAddress, + this.accounts.scheduler, + ); + await this.depositInIexecAccount(contributor, workerStakePerTask); + await IexecPoco2__factory.connect(this.proxyAddress, contributor) + .contribute( + taskId, + resultHash, + resultSeal, + enclaveAddress, + enclaveSignature, + schedulerSignature, + ) + .then((tx) => tx.wait()); + return { taskId, workerStakePerTask }; + } + async createWorkerpool() { const iexec = IexecAccessors__factory.connect(this.proxyAddress, this.accounts.anyone); const workerpoolRegistry: WorkerpoolRegistry = WorkerpoolRegistry__factory.connect( diff --git a/utils/poco-tools.ts b/utils/poco-tools.ts index ab5cd56f2..6b284a5c4 100644 --- a/utils/poco-tools.ts +++ b/utils/poco-tools.ts @@ -34,6 +34,11 @@ export enum OrderOperationEnum { CLOSE, } +export enum PocoMode { + CLASSIC, + BOOST, +} + export interface IexecAccounts { iexecAdmin: SignerWithAddress; requester: SignerWithAddress; @@ -42,14 +47,15 @@ export interface IexecAccounts { appProvider: SignerWithAddress; datasetProvider: SignerWithAddress; scheduler: SignerWithAddress; + enclave: SignerWithAddress; + sms: SignerWithAddress; + anyone: SignerWithAddress; worker: SignerWithAddress; worker1: SignerWithAddress; worker2: SignerWithAddress; worker3: SignerWithAddress; worker4: SignerWithAddress; - enclave: SignerWithAddress; - sms: SignerWithAddress; - anyone: SignerWithAddress; + worker5: SignerWithAddress; } export async function getIexecAccounts(): Promise { @@ -62,14 +68,15 @@ export async function getIexecAccounts(): Promise { appProvider: signers[4], datasetProvider: signers[5], scheduler: signers[6], - worker: signers[7], // same as worker1 - worker1: signers[7], - worker2: signers[8], - worker3: signers[9], - worker4: signers[10], - enclave: signers[11], - sms: signers[12], - anyone: signers[13], + enclave: signers[7], + sms: signers[8], + anyone: signers[9], + worker: signers[10], // same as worker1 + worker1: signers[10], + worker2: signers[11], + worker3: signers[12], + worker4: signers[13], + worker5: signers[14], }; }