From 79473245c1c406885f5010092e77d71320da9b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Fri, 14 Oct 2022 16:18:31 +0200 Subject: [PATCH 01/35] Init meta txs to Forwarder API --- package-lock.json | 31 +++++-- package.json | 2 +- src/contractLoader.ts | 20 +++-- src/dapp.ts | 153 ++++++++++++++++++++++++++++------- tests/contractLoader.test.ts | 16 ++-- tests/dapp.integ.test.ts | 42 +++++----- 6 files changed, 189 insertions(+), 75 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6eff936..55b2106 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.1.0", "license": "Apache-2.0", "dependencies": { - "@iexec/generic-oracle-contracts": "^2.0.0", + "@iexec/generic-oracle-contracts": "^2.1.0", "big.js": "^6.0.3", "ethers": "^5.6.8", "jsonpath": "^1.1.0", @@ -1341,9 +1341,12 @@ "dev": true }, "node_modules/@iexec/generic-oracle-contracts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@iexec/generic-oracle-contracts/-/generic-oracle-contracts-2.0.0.tgz", - "integrity": "sha512-Sigwmcm0VESaVijZwrsXq7ntjPMeDCXnL9QhYmjo/S0atcGSYIeVZaahDOZth4zzPs5gB7Eih7WzVgyV6NJ/tg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@iexec/generic-oracle-contracts/-/generic-oracle-contracts-2.1.0.tgz", + "integrity": "sha512-SXvJ4OHhu9GJUVqstOCqBISVxZ9KNNKpbMNrrJX1O53LxvxoDBgLAv0bEij+GjY1ANNDzKAbcUZZ0GDwA2JttQ==", + "dependencies": { + "openzeppelin-contracts-solc-0.8": "npm:@openzeppelin/contracts@^4.7.0" + }, "optionalDependencies": { "fsevents": "^2.3.2" } @@ -6277,6 +6280,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/openzeppelin-contracts-solc-0.8": { + "name": "@openzeppelin/contracts", + "version": "4.7.3", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.7.3.tgz", + "integrity": "sha512-dGRS0agJzu8ybo44pCIf3xBaPQN/65AIXNgK8+4gzKd5kbvlqyxryUYVLJv7fK98Seyd2hDZzVEHSWAh0Bt1Yw==" + }, "node_modules/optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -8800,11 +8809,12 @@ "dev": true }, "@iexec/generic-oracle-contracts": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@iexec/generic-oracle-contracts/-/generic-oracle-contracts-2.0.0.tgz", - "integrity": "sha512-Sigwmcm0VESaVijZwrsXq7ntjPMeDCXnL9QhYmjo/S0atcGSYIeVZaahDOZth4zzPs5gB7Eih7WzVgyV6NJ/tg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@iexec/generic-oracle-contracts/-/generic-oracle-contracts-2.1.0.tgz", + "integrity": "sha512-SXvJ4OHhu9GJUVqstOCqBISVxZ9KNNKpbMNrrJX1O53LxvxoDBgLAv0bEij+GjY1ANNDzKAbcUZZ0GDwA2JttQ==", "requires": { - "fsevents": "^2.3.2" + "fsevents": "^2.3.2", + "openzeppelin-contracts-solc-0.8": "npm:@openzeppelin/contracts@^4.7.0" } }, "@istanbuljs/load-nyc-config": { @@ -12568,6 +12578,11 @@ "mimic-fn": "^2.1.0" } }, + "openzeppelin-contracts-solc-0.8": { + "version": "npm:@openzeppelin/contracts@4.7.3", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.7.3.tgz", + "integrity": "sha512-dGRS0agJzu8ybo44pCIf3xBaPQN/65AIXNgK8+4gzKd5kbvlqyxryUYVLJv7fK98Seyd2hDZzVEHSWAh0Bt1Yw==" + }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", diff --git a/package.json b/package.json index c83d380..72ae5cf 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "This application is meant to build a docker container usable in SGX iexec tasks. The dapp take an input file containing a param set in a JSON format. The param set describe the request that should be done to the target API in order to get the wanted data.", "main": "src/app.js", "dependencies": { - "@iexec/generic-oracle-contracts": "^2.0.0", + "@iexec/generic-oracle-contracts": "^2.1.0", "big.js": "^6.0.3", "ethers": "^5.6.8", "jsonpath": "^1.1.0", diff --git a/src/contractLoader.ts b/src/contractLoader.ts index 5327181..188dc00 100644 --- a/src/contractLoader.ts +++ b/src/contractLoader.ts @@ -1,4 +1,4 @@ -import { ethers } from "ethers"; +import { ethers, Wallet } from "ethers"; import { ClassicOracle, ClassicOracle__factory, @@ -7,9 +7,10 @@ import { const chain = "goerli"; const oracleReceiver = "0x28291E6A81aC30cE6099144E68D8aEeE2b64052b"; -export function loadClassicOracle( +export function getWalletOnProvider( + chainId: number, encodedArgs: string | undefined -): ClassicOracle { +): Wallet { console.log("Target chain: " + chain); console.log("Target oracle address: " + oracleReceiver); @@ -26,6 +27,7 @@ export function loadClassicOracle( throw Error("Failed to parse appDeveloperSecret JSON"); } + //TODO: Remove infura keys at some point const infuraProjectId = appDeveloperSecretJson.infuraProjectId; if (infuraProjectId == undefined) { throw Error("Failed to parse `infuraProjectId` from decoded secret JSON"); @@ -41,15 +43,17 @@ export function loadClassicOracle( throw Error("Failed to parse `targetPrivateKey` from decoded secret JSON"); } - const provider = new ethers.providers.InfuraProvider(chain, { - projectId: infuraProjectId, - projectSecret: infuraProjectSecret, - }); + // const provider = new ethers.providers.InfuraProvider(chain, { + // projectId: infuraProjectId, + // projectSecret: infuraProjectSecret, + // }); + const provider = ethers.getDefaultProvider(chainId); const wallet = new ethers.Wallet(targetPrivateKey, provider); console.log("Target reporter wallet address: " + wallet.address); - return new ClassicOracle__factory().attach(oracleReceiver).connect(wallet); + //return new ClassicOracle__factory().attach(oracleReceiver).connect(wallet); + return wallet; } interface OracleArgs { diff --git a/src/dapp.ts b/src/dapp.ts index 9ce0b2c..e17e190 100644 --- a/src/dapp.ts +++ b/src/dapp.ts @@ -1,6 +1,6 @@ import fsPromises from "fs/promises"; import utils from "./utils"; -import { loadClassicOracle } from "../src/contractLoader"; +import { getWalletOnProvider } from "../src/contractLoader"; import { apiCall } from "./caller"; import { jsonParamSetSchema } from "./validators"; import { @@ -9,13 +9,27 @@ import { extractApiKey, } from "./requestConsistency"; import { encodeValue } from "./resultEncoder"; -import { ethers } from "ethers"; +import { ethers, Wallet } from "ethers"; +import { + ClassicOracle, + ClassicOracle__factory, +} from "@iexec/generic-oracle-contracts/typechain"; +import fetch from "node-fetch"; + +const forwarderAddress = "0xc83de370A0D1C99F3D3D9e77bd930520ded81fFA"; +const oracleReceiver = "0x8Ad317241854b1A29A06cE5478e6B92FA09Cd03a"; +const forwarderApiUrl = "http://localhost:3000"; +//TODO: Check input params +const chainId = 5; //goerli const start = async () => { - let classicOracle; + let wallet: Wallet; try { // validate args or exit before going further - classicOracle = loadClassicOracle(process.env.IEXEC_APP_DEVELOPER_SECRET); + wallet = getWalletOnProvider( + chainId, + process.env.IEXEC_APP_DEVELOPER_SECRET + ); } catch (e) { console.error("Failed to load ClassicOracle from encoded args [e:%s]", e); return undefined; @@ -82,32 +96,113 @@ const start = async () => { return undefined; } - try { - const tx = await classicOracle.receiveResult(oracleId, encodedValue); + const signedForwardRequest = await getSignedForwardRequest( + wallet, + oracleId, + encodedValue + ); + + const multiForwardRequest = { + requests: [signedForwardRequest], + }; + + console.log(JSON.stringify(multiForwardRequest)); + + if (await postMultiForwardRequest(multiForwardRequest, oracleId)) { + return encodedValue; + } + return undefined; +}; +export default start; + +async function postMultiForwardRequest( + multiForwardRequest: any, //TODO: Update to explciti type + oracleId: string +): Promise { + const response = await fetch(forwarderApiUrl + "/forward", { + method: "post", + body: JSON.stringify(multiForwardRequest), + headers: { "Content-Type": "application/json" }, + }); + + if (response.ok) { console.log( - "Sent transaction to targeted oracle [tx:%s, oracleId:%s, encodedValue:%s]", - tx.hash, + "Successful response from Forwarder API [oracleId:%s, taskId:%s]", oracleId, - encodedValue + oracleId ); - return tx - .wait() - .then((receipt) => { - console.log( - "Mined transaction for targeted oracle [tx:%s, blockNumber:%s, oracleId:%s]", - tx.hash, - receipt.blockNumber, - oracleId - ); - return encodedValue; - }) - .catch((e) => { - console.error("Failed transaction on targeted oracle [e:%s]", e); - return undefined; - }); - } catch (e) { - console.error("Failed to send transaction [e:%s]", e); - return undefined; + return true; } -}; -export default start; + console.error( + "Failure response from Forwarder API [oracleId:%s, taskId:%s, error:%s, data:%s]", + oracleId, + oracleId, + response.status, + await response.json() + ); + return false; +} + +async function getSignedForwardRequest( + wallet: ethers.Wallet, + oracleId: string, + encodedValue: string +) { + const reporterAddress = await wallet.getAddress(); + const domain = { + name: "SaltyForwarder", + version: "0.0.1", + chainId: chainId.toString(), + verifyingContract: forwarderAddress, + }; + const types = { + ForwardRequest: [ + { name: "from", type: "address" }, + { name: "to", type: "address" }, + { name: "value", type: "uint256" }, + { name: "gas", type: "uint256" }, + { name: "salt", type: "bytes32" }, + { name: "data", type: "bytes" }, + ], + }; + + const classicOracle = new ClassicOracle__factory() + .attach(oracleReceiver) + .connect(wallet); + + const forwardRequest = { + from: reporterAddress, + to: oracleReceiver, + value: "0", + //TODO: Change to receiveResult(taskId, ..) + gas: ( + await classicOracle.estimateGas.receiveResult(oracleId, encodedValue) + ).toString(), + salt: ethers.utils.keccak256( + ethers.utils.toUtf8Bytes(Math.random().toString()) + ), + data: classicOracle.interface.encodeFunctionData("receiveResult", [ + oracleId, + encodedValue, + ]), + }; + + const signature = await wallet._signTypedData(domain, types, forwardRequest); + + const signedForwardRequest = { + eip712: { + types: types.ForwardRequest, + domain: domain, + message: forwardRequest, + }, + sign: signature, + }; + console.log( + "Signed forwardRequest [oracleId:%s, taskId:%s, encodedValue:%s, signedForwardRequest:%s]", + oracleId, + oracleId, //TODO: Use taskId + encodedValue, + JSON.stringify(signedForwardRequest) + ); + return signedForwardRequest; +} diff --git a/tests/contractLoader.test.ts b/tests/contractLoader.test.ts index 5dbcff5..b2121b2 100644 --- a/tests/contractLoader.test.ts +++ b/tests/contractLoader.test.ts @@ -1,27 +1,27 @@ -import { loadClassicOracle } from "../src/contractLoader"; +import { getWalletOnProvider } from "../src/contractLoader"; describe("contract loader", () => { test("should fail since no args", () => { expect(() => { - loadClassicOracle(undefined); + getWalletOnProvider(undefined); }).toThrowError("Encoded args are required"); }); test("should fail since empty args", () => { expect(() => { - loadClassicOracle(""); + getWalletOnProvider(""); }).toThrowError("Failed to parse appDeveloperSecret JSON"); }); test("should fail since parse payload failed", () => { expect(() => { - loadClassicOracle(JSON.stringify({ some: "data" })); + getWalletOnProvider(JSON.stringify({ some: "data" })); }).toThrowError("Failed to parse appDeveloperSecret JSON"); }); test("should fail since no infuraProjectId", () => { expect(() => { - loadClassicOracle(encode({})); + getWalletOnProvider(encode({})); }).toThrowError( "Failed to parse `infuraProjectId` from decoded secret JSON" ); @@ -29,7 +29,7 @@ describe("contract loader", () => { test("should fail since no infuraProjectSecret", () => { expect(() => { - loadClassicOracle( + getWalletOnProvider( encode({ infuraProjectId: "id", }) @@ -41,7 +41,7 @@ describe("contract loader", () => { test("should fail since no targetPrivateKey", () => { expect(() => { - loadClassicOracle( + getWalletOnProvider( encode({ infuraProjectId: "id", infuraProjectSecret: "secret", @@ -54,7 +54,7 @@ describe("contract loader", () => { test("should return something", () => { expect( - loadClassicOracle( + getWalletOnProvider( encode({ infuraProjectId: "some", infuraProjectSecret: "secret", diff --git a/tests/dapp.integ.test.ts b/tests/dapp.integ.test.ts index 1298713..ee8dac4 100644 --- a/tests/dapp.integ.test.ts +++ b/tests/dapp.integ.test.ts @@ -4,31 +4,31 @@ const somePrivateKey = "0x0000000000000000000000000000000000000000000000000000000000000001"; describe("dapp", () => { - test("should fail since no app developer secret", async () => { - const callbackData = await Dapp(); - expect(callbackData).toBeUndefined(); - }); + // test("should fail since no app developer secret", async () => { + // const callbackData = await Dapp(); + // expect(callbackData).toBeUndefined(); + // }); - test("should fail since no inputfile", async () => { - process.env.IEXEC_APP_DEVELOPER_SECRET = - buildAppSecretWithValidInfuraProcessEnv(somePrivateKey); + // test("should fail since no inputfile", async () => { + // process.env.IEXEC_APP_DEVELOPER_SECRET = + // buildAppSecretWithValidInfuraProcessEnv(somePrivateKey); - const callbackData = await Dapp(); - expect(callbackData).toBeUndefined(); - }); + // const callbackData = await Dapp(); + // expect(callbackData).toBeUndefined(); + // }); - test("should fail since tx failed", async () => { - process.env.IEXEC_APP_DEVELOPER_SECRET = - buildAppSecretWithValidInfuraProcessEnv(somePrivateKey); - process.env.IEXEC_INPUT_FILES_NUMBER = "1"; - process.env.IEXEC_INPUT_FILES_FOLDER = "./tests/test_files"; - process.env.IEXEC_INPUT_FILE_NAME_1 = "input_file_no_dataset.json"; - process.env.IEXEC_OUT = "./tests/test_out"; - process.env.IEXEC_IN = "./tests/test_files"; + // test("should fail since tx failed", async () => { + // process.env.IEXEC_APP_DEVELOPER_SECRET = + // buildAppSecretWithValidInfuraProcessEnv(somePrivateKey); + // process.env.IEXEC_INPUT_FILES_NUMBER = "1"; + // process.env.IEXEC_INPUT_FILES_FOLDER = "./tests/test_files"; + // process.env.IEXEC_INPUT_FILE_NAME_1 = "input_file_no_dataset.json"; + // process.env.IEXEC_OUT = "./tests/test_out"; + // process.env.IEXEC_IN = "./tests/test_files"; - const callbackData = await Dapp(); - expect(callbackData).toBeUndefined(); - }); + // const callbackData = await Dapp(); + // expect(callbackData).toBeUndefined(); + // }); test("a full successful dapp IT run without dataset", async () => { dotEnvConfig(); From 5e7e4a741aef028f2369c71e35534b445c164342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Tue, 18 Oct 2022 17:22:26 +0200 Subject: [PATCH 02/35] Parse chain from argv --- src/contractLoader.ts | 6 +- src/dapp.ts | 177 +++++++++++---------------------- src/getSignedForwardRequest.ts | 69 +++++++++++++ src/postMultiForwardRequest.ts | 31 ++++++ tests/dapp.integ.test.ts | 3 + 5 files changed, 162 insertions(+), 124 deletions(-) create mode 100644 src/getSignedForwardRequest.ts create mode 100644 src/postMultiForwardRequest.ts diff --git a/src/contractLoader.ts b/src/contractLoader.ts index 188dc00..20e752f 100644 --- a/src/contractLoader.ts +++ b/src/contractLoader.ts @@ -4,15 +4,11 @@ import { ClassicOracle__factory, } from "@iexec/generic-oracle-contracts/typechain"; -const chain = "goerli"; -const oracleReceiver = "0x28291E6A81aC30cE6099144E68D8aEeE2b64052b"; - export function getWalletOnProvider( chainId: number, encodedArgs: string | undefined ): Wallet { - console.log("Target chain: " + chain); - console.log("Target oracle address: " + oracleReceiver); + console.log("Target chain: " + chainId); if (encodedArgs == undefined) { throw Error("Encoded args are required"); diff --git a/src/dapp.ts b/src/dapp.ts index e17e190..b8ee9f7 100644 --- a/src/dapp.ts +++ b/src/dapp.ts @@ -10,30 +10,22 @@ import { } from "./requestConsistency"; import { encodeValue } from "./resultEncoder"; import { ethers, Wallet } from "ethers"; -import { - ClassicOracle, - ClassicOracle__factory, -} from "@iexec/generic-oracle-contracts/typechain"; -import fetch from "node-fetch"; - -const forwarderAddress = "0xc83de370A0D1C99F3D3D9e77bd930520ded81fFA"; -const oracleReceiver = "0x8Ad317241854b1A29A06cE5478e6B92FA09Cd03a"; -const forwarderApiUrl = "http://localhost:3000"; -//TODO: Check input params -const chainId = 5; //goerli +import { getSignedForwardRequest } from "./getSignedForwardRequest"; +import { postMultiForwardRequest } from "./postMultiForwardRequest"; + +export const forwarderApiUrl = "http://localhost:3000"; +// Goerli +export const goerliForwarderAddress = + "0xc83de370A0D1C99F3D3D9e77bd930520ded81fFA"; +export const goerliOracleReceiver = + "0x8Ad317241854b1A29A06cE5478e6B92FA09Cd03a"; +export const supportedTargetChainIds = [ + 5, // goerli +]; +const supportedTargetChainId = supportedTargetChainIds[0]; +//TODO: Use same structure for things related to a chain const start = async () => { - let wallet: Wallet; - try { - // validate args or exit before going further - wallet = getWalletOnProvider( - chainId, - process.env.IEXEC_APP_DEVELOPER_SECRET - ); - } catch (e) { - console.error("Failed to load ClassicOracle from encoded args [e:%s]", e); - return undefined; - } const inputFolder = process.env.IEXEC_IN; let inputFilePath; try { @@ -96,113 +88,60 @@ const start = async () => { return undefined; } - const signedForwardRequest = await getSignedForwardRequest( - wallet, - oracleId, - encodedValue - ); - - const multiForwardRequest = { - requests: [signedForwardRequest], - }; - - console.log(JSON.stringify(multiForwardRequest)); - - if (await postMultiForwardRequest(multiForwardRequest, oracleId)) { - return encodedValue; + // `node app.ts bla` (0, 1, 2) + console.log(process.argv); + if ( + process.argv.length > 2 && + Number(process.argv[4]) == supportedTargetChainId //TODO: Only works in tests, update to 2 + ) { + const isCrossChainRequestSent = await triggerMultiFowardRequest( + supportedTargetChainId, + oracleId, + encodedValue + ); + // Status is logged for information purpose only (app must go on on failure) + console.log( + "isCrossChainRequestSent status [status:%s]", + isCrossChainRequestSent + ); } - return undefined; + return encodedValue; }; export default start; -async function postMultiForwardRequest( - multiForwardRequest: any, //TODO: Update to explciti type - oracleId: string -): Promise { - const response = await fetch(forwarderApiUrl + "/forward", { - method: "post", - body: JSON.stringify(multiForwardRequest), - headers: { "Content-Type": "application/json" }, - }); - - if (response.ok) { - console.log( - "Successful response from Forwarder API [oracleId:%s, taskId:%s]", - oracleId, - oracleId +async function triggerMultiFowardRequest( + targetChainId: number, + oracleId: string, + encodedValue: string +) { + const taskId = process.env.IEXEC_TASK_ID; + if (taskId == undefined) { + console.error("[IEXEC] IEXEC_TASK_ID is missing"); + return false; + } + let wallet: Wallet; + try { + // validate args or exit before going further + wallet = getWalletOnProvider( + targetChainId, + process.env.IEXEC_APP_DEVELOPER_SECRET ); - return true; + } catch (e) { + console.error("Failed to load ClassicOracle from encoded args [e:%s]", e); + return false; } - console.error( - "Failure response from Forwarder API [oracleId:%s, taskId:%s, error:%s, data:%s]", - oracleId, + const signedForwardRequest = await getSignedForwardRequest( + wallet, + taskId, oracleId, - response.status, - await response.json() + encodedValue ); - return false; -} -async function getSignedForwardRequest( - wallet: ethers.Wallet, - oracleId: string, - encodedValue: string -) { - const reporterAddress = await wallet.getAddress(); - const domain = { - name: "SaltyForwarder", - version: "0.0.1", - chainId: chainId.toString(), - verifyingContract: forwarderAddress, - }; - const types = { - ForwardRequest: [ - { name: "from", type: "address" }, - { name: "to", type: "address" }, - { name: "value", type: "uint256" }, - { name: "gas", type: "uint256" }, - { name: "salt", type: "bytes32" }, - { name: "data", type: "bytes" }, - ], - }; - - const classicOracle = new ClassicOracle__factory() - .attach(oracleReceiver) - .connect(wallet); - - const forwardRequest = { - from: reporterAddress, - to: oracleReceiver, - value: "0", - //TODO: Change to receiveResult(taskId, ..) - gas: ( - await classicOracle.estimateGas.receiveResult(oracleId, encodedValue) - ).toString(), - salt: ethers.utils.keccak256( - ethers.utils.toUtf8Bytes(Math.random().toString()) - ), - data: classicOracle.interface.encodeFunctionData("receiveResult", [ - oracleId, - encodedValue, - ]), + const multiForwardRequest = { + requests: [signedForwardRequest], }; - const signature = await wallet._signTypedData(domain, types, forwardRequest); + console.log(JSON.stringify(multiForwardRequest)); - const signedForwardRequest = { - eip712: { - types: types.ForwardRequest, - domain: domain, - message: forwardRequest, - }, - sign: signature, - }; - console.log( - "Signed forwardRequest [oracleId:%s, taskId:%s, encodedValue:%s, signedForwardRequest:%s]", - oracleId, - oracleId, //TODO: Use taskId - encodedValue, - JSON.stringify(signedForwardRequest) - ); - return signedForwardRequest; + return await postMultiForwardRequest(multiForwardRequest, oracleId, taskId); } diff --git a/src/getSignedForwardRequest.ts b/src/getSignedForwardRequest.ts new file mode 100644 index 0000000..0868156 --- /dev/null +++ b/src/getSignedForwardRequest.ts @@ -0,0 +1,69 @@ +import { ethers } from "ethers"; +import { ClassicOracle__factory } from "@iexec/generic-oracle-contracts/typechain"; +import { goerliForwarderAddress, goerliOracleReceiver } from "./dapp"; + +export async function getSignedForwardRequest( + wallet: ethers.Wallet, + taskId: string, + oracleId: string, + encodedValue: string +) { + const reporterAddress = await wallet.getAddress(); + const domain = { + name: "SaltyForwarder", + version: "0.0.1", + chainId: (await wallet.getChainId()).toString(), //Use id from arg instead? + verifyingContract: goerliForwarderAddress, + }; + const types = { + ForwardRequest: [ + { name: "from", type: "address" }, + { name: "to", type: "address" }, + { name: "value", type: "uint256" }, + { name: "gas", type: "uint256" }, + { name: "salt", type: "bytes32" }, + { name: "data", type: "bytes" }, + ], + }; + + console.log("Target oracle address: " + goerliOracleReceiver); + + const classicOracle = new ClassicOracle__factory() + .attach(goerliOracleReceiver) + .connect(wallet); + + const forwardRequest = { + from: reporterAddress, + to: goerliOracleReceiver, + value: "0", + gas: ( + await classicOracle.estimateGas.receiveResult(taskId, encodedValue) + ).toString(), + salt: ethers.utils.keccak256( + ethers.utils.toUtf8Bytes(Math.random().toString()) + ), + data: classicOracle.interface.encodeFunctionData("receiveResult", [ + taskId, + encodedValue, + ]), + }; + + const signature = await wallet._signTypedData(domain, types, forwardRequest); + + const signedForwardRequest = { + eip712: { + types: types.ForwardRequest, + domain: domain, + message: forwardRequest, + }, + sign: signature, + }; + console.log( + "Signed forwardRequest [oracleId:%s, taskId:%s, encodedValue:%s, signedForwardRequest:%s]", + oracleId, + taskId, + encodedValue, + JSON.stringify(signedForwardRequest) + ); + return signedForwardRequest; +} diff --git a/src/postMultiForwardRequest.ts b/src/postMultiForwardRequest.ts new file mode 100644 index 0000000..112d893 --- /dev/null +++ b/src/postMultiForwardRequest.ts @@ -0,0 +1,31 @@ +import fetch from "node-fetch"; +import { forwarderApiUrl } from "./dapp"; + +export async function postMultiForwardRequest( + multiForwardRequest: any, + oracleId: string, + taskId: string +): Promise { + const response = await fetch(forwarderApiUrl + "/forward", { + method: "post", + body: JSON.stringify(multiForwardRequest), + headers: { "Content-Type": "application/json" }, + }); + + if (response.ok) { + console.log( + "Successful response from Forwarder API [oracleId:%s, taskId:%s]", + oracleId, + taskId + ); + return true; + } + console.error( + "Failure response from Forwarder API [oracleId:%s, taskId:%s, error:%s, data:%s]", + oracleId, + taskId, + response.status, + await response.json().then((aa) => console.log(aa.message)) + ); + return false; +} diff --git a/tests/dapp.integ.test.ts b/tests/dapp.integ.test.ts index ee8dac4..730f62d 100644 --- a/tests/dapp.integ.test.ts +++ b/tests/dapp.integ.test.ts @@ -32,6 +32,9 @@ describe("dapp", () => { test("a full successful dapp IT run without dataset", async () => { dotEnvConfig(); + process.argv.push("5"); // goerli + process.env.IEXEC_TASK_ID = + "0x0000000000000000000000000000000000000000000000000000000000000abc"; process.env.IEXEC_APP_DEVELOPER_SECRET = buildAppSecretWithValidInfuraProcessEnv(process.env.TARGET_PRIVATE_KEY); process.env.IEXEC_INPUT_FILES_NUMBER = "1"; From 87257f12fa6dab3bd36ae4b5b15bf7933bf61a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Wed, 19 Oct 2022 16:09:45 +0200 Subject: [PATCH 03/35] Support multiple target chains --- src/dapp.ts | 68 +++--------------- src/forward/forwardEnvironment.ts | 30 ++++++++ src/forward/forwardHandler.ts | 72 +++++++++++++++++++ .../forwardSender.ts} | 2 +- .../forwardSigner.ts} | 19 ++--- .../walletLoader.ts} | 19 ++--- tests/contractLoader.test.ts | 16 ++--- tests/dapp.integ.test.ts | 2 +- 8 files changed, 144 insertions(+), 84 deletions(-) create mode 100644 src/forward/forwardEnvironment.ts create mode 100644 src/forward/forwardHandler.ts rename src/{postMultiForwardRequest.ts => forward/forwardSender.ts} (93%) rename src/{getSignedForwardRequest.ts => forward/forwardSigner.ts} (75%) rename src/{contractLoader.ts => forward/walletLoader.ts} (83%) diff --git a/src/dapp.ts b/src/dapp.ts index b8ee9f7..adf729b 100644 --- a/src/dapp.ts +++ b/src/dapp.ts @@ -1,6 +1,5 @@ import fsPromises from "fs/promises"; import utils from "./utils"; -import { getWalletOnProvider } from "../src/contractLoader"; import { apiCall } from "./caller"; import { jsonParamSetSchema } from "./validators"; import { @@ -9,21 +8,8 @@ import { extractApiKey, } from "./requestConsistency"; import { encodeValue } from "./resultEncoder"; -import { ethers, Wallet } from "ethers"; -import { getSignedForwardRequest } from "./getSignedForwardRequest"; -import { postMultiForwardRequest } from "./postMultiForwardRequest"; - -export const forwarderApiUrl = "http://localhost:3000"; -// Goerli -export const goerliForwarderAddress = - "0xc83de370A0D1C99F3D3D9e77bd930520ded81fFA"; -export const goerliOracleReceiver = - "0x8Ad317241854b1A29A06cE5478e6B92FA09Cd03a"; -export const supportedTargetChainIds = [ - 5, // goerli -]; -const supportedTargetChainId = supportedTargetChainIds[0]; -//TODO: Use same structure for things related to a chain +import { ethers } from "ethers"; +import { triggerMultiFowardRequest } from "./forward/forwardHandler"; const start = async () => { const inputFolder = process.env.IEXEC_IN; @@ -90,12 +76,15 @@ const start = async () => { // `node app.ts bla` (0, 1, 2) console.log(process.argv); - if ( - process.argv.length > 2 && - Number(process.argv[4]) == supportedTargetChainId //TODO: Only works in tests, update to 2 - ) { + const requestedChainIds = + process.argv.length > 2 + ? // Parse chainIds, sort them, remove duplicates, cast them from string to number + Array.from(new Set(process.argv[4].split(",").sort()), Number) //TODO: Only works in tests, update to 2 + : undefined; + if (requestedChainIds) { + console.log("Crosschain requested [chains:%s]", requestedChainIds); const isCrossChainRequestSent = await triggerMultiFowardRequest( - supportedTargetChainId, + requestedChainIds, oracleId, encodedValue ); @@ -108,40 +97,3 @@ const start = async () => { return encodedValue; }; export default start; - -async function triggerMultiFowardRequest( - targetChainId: number, - oracleId: string, - encodedValue: string -) { - const taskId = process.env.IEXEC_TASK_ID; - if (taskId == undefined) { - console.error("[IEXEC] IEXEC_TASK_ID is missing"); - return false; - } - let wallet: Wallet; - try { - // validate args or exit before going further - wallet = getWalletOnProvider( - targetChainId, - process.env.IEXEC_APP_DEVELOPER_SECRET - ); - } catch (e) { - console.error("Failed to load ClassicOracle from encoded args [e:%s]", e); - return false; - } - const signedForwardRequest = await getSignedForwardRequest( - wallet, - taskId, - oracleId, - encodedValue - ); - - const multiForwardRequest = { - requests: [signedForwardRequest], - }; - - console.log(JSON.stringify(multiForwardRequest)); - - return await postMultiForwardRequest(multiForwardRequest, oracleId, taskId); -} diff --git a/src/forward/forwardEnvironment.ts b/src/forward/forwardEnvironment.ts new file mode 100644 index 0000000..36a33ee --- /dev/null +++ b/src/forward/forwardEnvironment.ts @@ -0,0 +1,30 @@ +export const forwarderApiUrl = "http://localhost:3000"; + +const chainIdToOnChainConfig = new Map([ + [ + 5, // Goerli + { + forwarder: "0xc83de370A0D1C99F3D3D9e77bd930520ded81fFA", + oracle: "0x8Ad317241854b1A29A06cE5478e6B92FA09Cd03a", + providerUrl: undefined, //use default provider from ethers + }, + ], + [ + 80001, // Mumbai Polygon + { + forwarder: "0x6843aA5A3a777Ae750DD9d93a9D0fdF99e061b53", + oracle: "0x68bDfa911178f72CEA7BCFd0FeEbbA4cDDE24eCF", + providerUrl: "https://rpc-mumbai.maticvigil.com", + }, + ], +]); + +export function getOnChainConfig(chainId: number) { + return chainIdToOnChainConfig.get(chainId); +} + +export interface OnChainConfig { + forwarder: string; + oracle: string; + providerUrl: string | undefined; +} diff --git a/src/forward/forwardHandler.ts b/src/forward/forwardHandler.ts new file mode 100644 index 0000000..cba41c0 --- /dev/null +++ b/src/forward/forwardHandler.ts @@ -0,0 +1,72 @@ +import { getWalletWithProvider } from "./walletLoader"; +import { Wallet } from "ethers"; +import { getSignedForwardRequest } from "./forwardSigner"; +import { postMultiForwardRequest } from "./forwardSender"; +import { getOnChainConfig } from "./forwardEnvironment"; + +export async function triggerMultiFowardRequest( + requestedChainIds: number[], + oracleId: string, + encodedValue: string +) { + const taskId = process.env.IEXEC_TASK_ID; + if (taskId == undefined) { + console.error("[IEXEC] IEXEC_TASK_ID is missing"); + return false; + } + + const chainIds = requestedChainIds.filter(isSupportedChain); + const signedForwardRequests = []; + + for (const chainId of chainIds) { + const onChainConfig = getOnChainConfig(chainId); + if (!onChainConfig) { + // already checked, should not happen + console.error("Chain not supported [chainId:%s]", chainId); + continue; + } + + let wallet: Wallet; + try { + // validate args or exit before going further + wallet = getWalletWithProvider( + chainId, + process.env.IEXEC_APP_DEVELOPER_SECRET, + onChainConfig.providerUrl + ); + } catch (e) { + console.error("Failed to load ClassicOracle from encoded args [e:%s]", e); + continue; + } + + const signedForwardRequest = await getSignedForwardRequest( + wallet, + taskId, + oracleId, + encodedValue, + onChainConfig + ); + + signedForwardRequests.push(signedForwardRequest); + } + + if (signedForwardRequests.length == 0) { + return false; + } + + const multiForwardRequest = { + requests: signedForwardRequests, + }; + + console.log(JSON.stringify(multiForwardRequest)); + + return await postMultiForwardRequest(multiForwardRequest, oracleId, taskId); +} + +function isSupportedChain(chainId: number) { + if (getOnChainConfig(chainId) != undefined) { + return true; + } + console.error("Chain not supported [chainId:%s]", chainId); + return false; +} diff --git a/src/postMultiForwardRequest.ts b/src/forward/forwardSender.ts similarity index 93% rename from src/postMultiForwardRequest.ts rename to src/forward/forwardSender.ts index 112d893..6b9c31c 100644 --- a/src/postMultiForwardRequest.ts +++ b/src/forward/forwardSender.ts @@ -1,5 +1,5 @@ import fetch from "node-fetch"; -import { forwarderApiUrl } from "./dapp"; +import { forwarderApiUrl } from "./forwardEnvironment"; export async function postMultiForwardRequest( multiForwardRequest: any, diff --git a/src/getSignedForwardRequest.ts b/src/forward/forwardSigner.ts similarity index 75% rename from src/getSignedForwardRequest.ts rename to src/forward/forwardSigner.ts index 0868156..e242f40 100644 --- a/src/getSignedForwardRequest.ts +++ b/src/forward/forwardSigner.ts @@ -1,19 +1,21 @@ import { ethers } from "ethers"; import { ClassicOracle__factory } from "@iexec/generic-oracle-contracts/typechain"; -import { goerliForwarderAddress, goerliOracleReceiver } from "./dapp"; +import { OnChainConfig } from "./forwardEnvironment"; export async function getSignedForwardRequest( wallet: ethers.Wallet, taskId: string, oracleId: string, - encodedValue: string + encodedValue: string, + envAddresses: OnChainConfig ) { + const chainId = (await wallet.getChainId()).toString(); //Use id from arg instead? const reporterAddress = await wallet.getAddress(); const domain = { name: "SaltyForwarder", version: "0.0.1", - chainId: (await wallet.getChainId()).toString(), //Use id from arg instead? - verifyingContract: goerliForwarderAddress, + chainId: chainId, + verifyingContract: envAddresses.forwarder, }; const types = { ForwardRequest: [ @@ -26,15 +28,15 @@ export async function getSignedForwardRequest( ], }; - console.log("Target oracle address: " + goerliOracleReceiver); + const oracleAddress = envAddresses.oracle; const classicOracle = new ClassicOracle__factory() - .attach(goerliOracleReceiver) + .attach(oracleAddress) .connect(wallet); const forwardRequest = { from: reporterAddress, - to: goerliOracleReceiver, + to: oracleAddress, value: "0", gas: ( await classicOracle.estimateGas.receiveResult(taskId, encodedValue) @@ -59,7 +61,8 @@ export async function getSignedForwardRequest( sign: signature, }; console.log( - "Signed forwardRequest [oracleId:%s, taskId:%s, encodedValue:%s, signedForwardRequest:%s]", + "Signed forwardRequest [chainId:%s, oracleId:%s, taskId:%s, encodedValue:%s, signedForwardRequest:%s]", + chainId, oracleId, taskId, encodedValue, diff --git a/src/contractLoader.ts b/src/forward/walletLoader.ts similarity index 83% rename from src/contractLoader.ts rename to src/forward/walletLoader.ts index 20e752f..f933886 100644 --- a/src/contractLoader.ts +++ b/src/forward/walletLoader.ts @@ -1,12 +1,9 @@ import { ethers, Wallet } from "ethers"; -import { - ClassicOracle, - ClassicOracle__factory, -} from "@iexec/generic-oracle-contracts/typechain"; -export function getWalletOnProvider( +export function getWalletWithProvider( chainId: number, - encodedArgs: string | undefined + encodedArgs: string | undefined, + providerUrl: string | undefined ): Wallet { console.log("Target chain: " + chainId); @@ -39,16 +36,22 @@ export function getWalletOnProvider( throw Error("Failed to parse `targetPrivateKey` from decoded secret JSON"); } + //TODO: Remove if default provider is fine // const provider = new ethers.providers.InfuraProvider(chain, { // projectId: infuraProjectId, // projectSecret: infuraProjectSecret, // }); - const provider = ethers.getDefaultProvider(chainId); + + let provider; + if (providerUrl) { + provider = new ethers.providers.JsonRpcProvider(providerUrl); + } else { + provider = ethers.getDefaultProvider(chainId); + } const wallet = new ethers.Wallet(targetPrivateKey, provider); console.log("Target reporter wallet address: " + wallet.address); - //return new ClassicOracle__factory().attach(oracleReceiver).connect(wallet); return wallet; } diff --git a/tests/contractLoader.test.ts b/tests/contractLoader.test.ts index b2121b2..4873688 100644 --- a/tests/contractLoader.test.ts +++ b/tests/contractLoader.test.ts @@ -1,27 +1,27 @@ -import { getWalletOnProvider } from "../src/contractLoader"; +import { getWalletWithProvider } from "../src/forward/walletLoader"; describe("contract loader", () => { test("should fail since no args", () => { expect(() => { - getWalletOnProvider(undefined); + getWalletWithProvider(undefined); }).toThrowError("Encoded args are required"); }); test("should fail since empty args", () => { expect(() => { - getWalletOnProvider(""); + getWalletWithProvider(""); }).toThrowError("Failed to parse appDeveloperSecret JSON"); }); test("should fail since parse payload failed", () => { expect(() => { - getWalletOnProvider(JSON.stringify({ some: "data" })); + getWalletWithProvider(JSON.stringify({ some: "data" })); }).toThrowError("Failed to parse appDeveloperSecret JSON"); }); test("should fail since no infuraProjectId", () => { expect(() => { - getWalletOnProvider(encode({})); + getWalletWithProvider(encode({})); }).toThrowError( "Failed to parse `infuraProjectId` from decoded secret JSON" ); @@ -29,7 +29,7 @@ describe("contract loader", () => { test("should fail since no infuraProjectSecret", () => { expect(() => { - getWalletOnProvider( + getWalletWithProvider( encode({ infuraProjectId: "id", }) @@ -41,7 +41,7 @@ describe("contract loader", () => { test("should fail since no targetPrivateKey", () => { expect(() => { - getWalletOnProvider( + getWalletWithProvider( encode({ infuraProjectId: "id", infuraProjectSecret: "secret", @@ -54,7 +54,7 @@ describe("contract loader", () => { test("should return something", () => { expect( - getWalletOnProvider( + getWalletWithProvider( encode({ infuraProjectId: "some", infuraProjectSecret: "secret", diff --git a/tests/dapp.integ.test.ts b/tests/dapp.integ.test.ts index 730f62d..ecde04e 100644 --- a/tests/dapp.integ.test.ts +++ b/tests/dapp.integ.test.ts @@ -32,7 +32,7 @@ describe("dapp", () => { test("a full successful dapp IT run without dataset", async () => { dotEnvConfig(); - process.argv.push("5"); // goerli + process.argv.push("5,80001"); // Goerli & Mumbai Polygon process.env.IEXEC_TASK_ID = "0x0000000000000000000000000000000000000000000000000000000000000abc"; process.env.IEXEC_APP_DEVELOPER_SECRET = From e9c35dc9dc4bd59c63b1ff6e735dfea332da2cec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Thu, 20 Oct 2022 10:59:29 +0200 Subject: [PATCH 04/35] Add forward handler tests --- src/forward/forwardHandler.ts | 9 ++- src/forward/walletLoader.ts | 1 - tests/forward/forwardHandler.test.ts | 109 +++++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 4 deletions(-) create mode 100644 tests/forward/forwardHandler.test.ts diff --git a/src/forward/forwardHandler.ts b/src/forward/forwardHandler.ts index cba41c0..ef96307 100644 --- a/src/forward/forwardHandler.ts +++ b/src/forward/forwardHandler.ts @@ -1,8 +1,8 @@ import { getWalletWithProvider } from "./walletLoader"; import { Wallet } from "ethers"; +import { getOnChainConfig } from "./forwardEnvironment"; import { getSignedForwardRequest } from "./forwardSigner"; import { postMultiForwardRequest } from "./forwardSender"; -import { getOnChainConfig } from "./forwardEnvironment"; export async function triggerMultiFowardRequest( requestedChainIds: number[], @@ -10,7 +10,7 @@ export async function triggerMultiFowardRequest( encodedValue: string ) { const taskId = process.env.IEXEC_TASK_ID; - if (taskId == undefined) { + if (!taskId) { console.error("[IEXEC] IEXEC_TASK_ID is missing"); return false; } @@ -58,7 +58,10 @@ export async function triggerMultiFowardRequest( requests: signedForwardRequests, }; - console.log(JSON.stringify(multiForwardRequest)); + console.log( + "Multi foward request ready [request:%s]", + JSON.stringify(multiForwardRequest) + ); return await postMultiForwardRequest(multiForwardRequest, oracleId, taskId); } diff --git a/src/forward/walletLoader.ts b/src/forward/walletLoader.ts index f933886..1f7f39d 100644 --- a/src/forward/walletLoader.ts +++ b/src/forward/walletLoader.ts @@ -36,7 +36,6 @@ export function getWalletWithProvider( throw Error("Failed to parse `targetPrivateKey` from decoded secret JSON"); } - //TODO: Remove if default provider is fine // const provider = new ethers.providers.InfuraProvider(chain, { // projectId: infuraProjectId, // projectSecret: infuraProjectSecret, diff --git a/tests/forward/forwardHandler.test.ts b/tests/forward/forwardHandler.test.ts new file mode 100644 index 0000000..755dbd6 --- /dev/null +++ b/tests/forward/forwardHandler.test.ts @@ -0,0 +1,109 @@ +import { Wallet } from "ethers"; +import { triggerMultiFowardRequest } from "../../src/forward/forwardHandler"; +import { getWalletWithProvider } from "../../src/forward/walletLoader"; +import { getSignedForwardRequest } from "../../src/forward/forwardSigner"; +import { postMultiForwardRequest } from "../../src/forward/forwardSender"; + +jest.mock("../../src/forward/walletLoader"); +jest.mock("../../src/forward/forwardSigner"); +jest.mock("../../src/forward/forwardSender"); + +const getWalletWithProviderMock = getWalletWithProvider as jest.MockedFunction< + typeof getWalletWithProvider +>; +const getSignedForwardRequestMock = + getSignedForwardRequest as jest.MockedFunction< + typeof getSignedForwardRequest + >; +const postMultiForwardRequestMock = + postMultiForwardRequest as jest.MockedFunction< + typeof postMultiForwardRequest + >; + +const TASK_ID = + "0x0000000000000000000000000000000000000000000000000000000000000abc"; +const CHAIN_ID = 5; +const ORACLE_ID = "0x1"; +const VALUE = "0x2"; +const wallet = new Wallet( + "0x0000000000000000000000000000000000000000000000000000000000000001" +); + +describe("Forward handler", () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + test("should not trigger since task ID missing", async () => { + process.env.IEXEC_TASK_ID = ""; + + const success = await triggerMultiFowardRequest( + [CHAIN_ID], + ORACLE_ID, + VALUE + ); + expect(success).toBeFalsy(); + }); + + test("should not trigger since chain not supported", async () => { + process.env.IEXEC_TASK_ID = TASK_ID; + + const success = await triggerMultiFowardRequest( + [12345], // chain not supported + ORACLE_ID, + VALUE + ); + expect(success).toBeFalsy(); + }); + + test("should not trigger since wallet not found", async () => { + process.env.IEXEC_TASK_ID = TASK_ID; + + getWalletWithProviderMock.mockImplementation(() => { + throw new Error("Wallet error"); + }); + + const success = await triggerMultiFowardRequest( + [CHAIN_ID], + ORACLE_ID, + VALUE + ); + expect(success).toBeFalsy(); + }); + + test("should trigger", async () => { + process.env.IEXEC_TASK_ID = TASK_ID; + + getWalletWithProviderMock.mockReturnValue(wallet); + getSignedForwardRequestMock.mockReturnValue( + Promise.resolve({ + eip712: { + types: [], + domain: { + name: "string", + version: "string", + chainId: "string", + verifyingContract: "string", + }, + message: { + from: "string", + to: "string", + value: "string", + gas: "string", + salt: "string", + data: "string", + }, + }, + sign: "string", + }) + ); + postMultiForwardRequestMock.mockReturnValue(Promise.resolve(true)); + + const success = await triggerMultiFowardRequest( + [CHAIN_ID], + ORACLE_ID, + VALUE + ); + expect(success).toBeTruthy(); + }); +}); From e08b0c0e44ec64bce5fa8b7fd89da37290f86986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Thu, 20 Oct 2022 12:29:02 +0200 Subject: [PATCH 05/35] Update wallet loader tests --- .../walletLoader.test.ts} | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) rename tests/{contractLoader.test.ts => forward/walletLoader.test.ts} (63%) diff --git a/tests/contractLoader.test.ts b/tests/forward/walletLoader.test.ts similarity index 63% rename from tests/contractLoader.test.ts rename to tests/forward/walletLoader.test.ts index 4873688..e12f4e1 100644 --- a/tests/contractLoader.test.ts +++ b/tests/forward/walletLoader.test.ts @@ -1,27 +1,34 @@ -import { getWalletWithProvider } from "../src/forward/walletLoader"; +import { getWalletWithProvider } from "../../src/forward/walletLoader"; + +const CHAIN_ID = 5; +const PROVIDER = undefined; describe("contract loader", () => { test("should fail since no args", () => { expect(() => { - getWalletWithProvider(undefined); + getWalletWithProvider(CHAIN_ID, undefined, PROVIDER); }).toThrowError("Encoded args are required"); }); test("should fail since empty args", () => { expect(() => { - getWalletWithProvider(""); + getWalletWithProvider(CHAIN_ID, "", PROVIDER); }).toThrowError("Failed to parse appDeveloperSecret JSON"); }); test("should fail since parse payload failed", () => { expect(() => { - getWalletWithProvider(JSON.stringify({ some: "data" })); + getWalletWithProvider( + CHAIN_ID, + JSON.stringify({ some: "data" }), + PROVIDER + ); }).toThrowError("Failed to parse appDeveloperSecret JSON"); }); test("should fail since no infuraProjectId", () => { expect(() => { - getWalletWithProvider(encode({})); + getWalletWithProvider(CHAIN_ID, encode({}), PROVIDER); }).toThrowError( "Failed to parse `infuraProjectId` from decoded secret JSON" ); @@ -30,9 +37,11 @@ describe("contract loader", () => { test("should fail since no infuraProjectSecret", () => { expect(() => { getWalletWithProvider( + CHAIN_ID, encode({ infuraProjectId: "id", - }) + }), + PROVIDER ); }).toThrowError( "Failed to parse `infuraProjectSecret` from decoded secret JSON" @@ -42,27 +51,32 @@ describe("contract loader", () => { test("should fail since no targetPrivateKey", () => { expect(() => { getWalletWithProvider( + CHAIN_ID, encode({ infuraProjectId: "id", infuraProjectSecret: "secret", - }) + }), + PROVIDER ); }).toThrowError( "Failed to parse `targetPrivateKey` from decoded secret JSON" ); }); - test("should return something", () => { - expect( - getWalletWithProvider( - encode({ - infuraProjectId: "some", - infuraProjectSecret: "secret", - targetPrivateKey: - "0x0000000000000000000000000000000000000000000000000000000000000001", - }) - ) - ).not.toBeNull(); + test("should return wallet", async () => { + const wallet = getWalletWithProvider( + CHAIN_ID, + encode({ + infuraProjectId: "some", + infuraProjectSecret: "secret", + targetPrivateKey: + "0x0000000000000000000000000000000000000000000000000000000000000001", + }), + PROVIDER + ); + expect(await wallet.getAddress()).toEqual( + "0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf" + ); }); }); From 3a369fdf285d81ada4ba201856ebb138c79f8de0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Fri, 21 Oct 2022 11:54:34 +0200 Subject: [PATCH 06/35] Connect oracle contract with provider directly --- src/forward/forwardHandler.ts | 9 +++----- src/forward/forwardSigner.ts | 24 ++++++++++++++------ src/forward/walletLoader.ts | 17 ++------------- tests/forward/walletLoader.test.ts | 35 ++++++++++-------------------- 4 files changed, 33 insertions(+), 52 deletions(-) diff --git a/src/forward/forwardHandler.ts b/src/forward/forwardHandler.ts index ef96307..9ae7b7c 100644 --- a/src/forward/forwardHandler.ts +++ b/src/forward/forwardHandler.ts @@ -1,4 +1,4 @@ -import { getWalletWithProvider } from "./walletLoader"; +import { loadWallet } from "./walletLoader"; import { Wallet } from "ethers"; import { getOnChainConfig } from "./forwardEnvironment"; import { getSignedForwardRequest } from "./forwardSigner"; @@ -29,17 +29,14 @@ export async function triggerMultiFowardRequest( let wallet: Wallet; try { // validate args or exit before going further - wallet = getWalletWithProvider( - chainId, - process.env.IEXEC_APP_DEVELOPER_SECRET, - onChainConfig.providerUrl - ); + wallet = loadWallet(process.env.IEXEC_APP_DEVELOPER_SECRET); } catch (e) { console.error("Failed to load ClassicOracle from encoded args [e:%s]", e); continue; } const signedForwardRequest = await getSignedForwardRequest( + chainId, wallet, taskId, oracleId, diff --git a/src/forward/forwardSigner.ts b/src/forward/forwardSigner.ts index e242f40..75e0888 100644 --- a/src/forward/forwardSigner.ts +++ b/src/forward/forwardSigner.ts @@ -3,19 +3,19 @@ import { ClassicOracle__factory } from "@iexec/generic-oracle-contracts/typechai import { OnChainConfig } from "./forwardEnvironment"; export async function getSignedForwardRequest( + chainId: number, wallet: ethers.Wallet, taskId: string, oracleId: string, encodedValue: string, - envAddresses: OnChainConfig + onChainConfig: OnChainConfig ) { - const chainId = (await wallet.getChainId()).toString(); //Use id from arg instead? const reporterAddress = await wallet.getAddress(); const domain = { name: "SaltyForwarder", version: "0.0.1", - chainId: chainId, - verifyingContract: envAddresses.forwarder, + chainId: chainId.toString(), + verifyingContract: onChainConfig.forwarder, }; const types = { ForwardRequest: [ @@ -28,18 +28,28 @@ export async function getSignedForwardRequest( ], }; - const oracleAddress = envAddresses.oracle; + const oracleAddress = onChainConfig.oracle; + const providerUrl = onChainConfig.providerUrl; + + let provider; + if (providerUrl) { + provider = new ethers.providers.JsonRpcProvider(providerUrl); + } else { + provider = ethers.getDefaultProvider(chainId); + } const classicOracle = new ClassicOracle__factory() .attach(oracleAddress) - .connect(wallet); + .connect(provider); const forwardRequest = { from: reporterAddress, to: oracleAddress, value: "0", gas: ( - await classicOracle.estimateGas.receiveResult(taskId, encodedValue) + await classicOracle.estimateGas.receiveResult(taskId, encodedValue, { + from: reporterAddress, + }) ).toString(), salt: ethers.utils.keccak256( ethers.utils.toUtf8Bytes(Math.random().toString()) diff --git a/src/forward/walletLoader.ts b/src/forward/walletLoader.ts index 1f7f39d..aaf7d7f 100644 --- a/src/forward/walletLoader.ts +++ b/src/forward/walletLoader.ts @@ -1,12 +1,6 @@ import { ethers, Wallet } from "ethers"; -export function getWalletWithProvider( - chainId: number, - encodedArgs: string | undefined, - providerUrl: string | undefined -): Wallet { - console.log("Target chain: " + chainId); - +export function loadWallet(encodedArgs: string | undefined): Wallet { if (encodedArgs == undefined) { throw Error("Encoded args are required"); } @@ -41,14 +35,7 @@ export function getWalletWithProvider( // projectSecret: infuraProjectSecret, // }); - let provider; - if (providerUrl) { - provider = new ethers.providers.JsonRpcProvider(providerUrl); - } else { - provider = ethers.getDefaultProvider(chainId); - } - - const wallet = new ethers.Wallet(targetPrivateKey, provider); + const wallet = new ethers.Wallet(targetPrivateKey); console.log("Target reporter wallet address: " + wallet.address); return wallet; diff --git a/tests/forward/walletLoader.test.ts b/tests/forward/walletLoader.test.ts index e12f4e1..054b255 100644 --- a/tests/forward/walletLoader.test.ts +++ b/tests/forward/walletLoader.test.ts @@ -1,34 +1,27 @@ -import { getWalletWithProvider } from "../../src/forward/walletLoader"; - -const CHAIN_ID = 5; -const PROVIDER = undefined; +import { loadWallet } from "../../src/forward/walletLoader"; describe("contract loader", () => { test("should fail since no args", () => { expect(() => { - getWalletWithProvider(CHAIN_ID, undefined, PROVIDER); + loadWallet(undefined); }).toThrowError("Encoded args are required"); }); test("should fail since empty args", () => { expect(() => { - getWalletWithProvider(CHAIN_ID, "", PROVIDER); + loadWallet(""); }).toThrowError("Failed to parse appDeveloperSecret JSON"); }); test("should fail since parse payload failed", () => { expect(() => { - getWalletWithProvider( - CHAIN_ID, - JSON.stringify({ some: "data" }), - PROVIDER - ); + loadWallet(JSON.stringify({ some: "data" })); }).toThrowError("Failed to parse appDeveloperSecret JSON"); }); test("should fail since no infuraProjectId", () => { expect(() => { - getWalletWithProvider(CHAIN_ID, encode({}), PROVIDER); + loadWallet(encode({})); }).toThrowError( "Failed to parse `infuraProjectId` from decoded secret JSON" ); @@ -36,12 +29,10 @@ describe("contract loader", () => { test("should fail since no infuraProjectSecret", () => { expect(() => { - getWalletWithProvider( - CHAIN_ID, + loadWallet( encode({ infuraProjectId: "id", - }), - PROVIDER + }) ); }).toThrowError( "Failed to parse `infuraProjectSecret` from decoded secret JSON" @@ -50,13 +41,11 @@ describe("contract loader", () => { test("should fail since no targetPrivateKey", () => { expect(() => { - getWalletWithProvider( - CHAIN_ID, + loadWallet( encode({ infuraProjectId: "id", infuraProjectSecret: "secret", - }), - PROVIDER + }) ); }).toThrowError( "Failed to parse `targetPrivateKey` from decoded secret JSON" @@ -64,15 +53,13 @@ describe("contract loader", () => { }); test("should return wallet", async () => { - const wallet = getWalletWithProvider( - CHAIN_ID, + const wallet = loadWallet( encode({ infuraProjectId: "some", infuraProjectSecret: "secret", targetPrivateKey: "0x0000000000000000000000000000000000000000000000000000000000000001", - }), - PROVIDER + }) ); expect(await wallet.getAddress()).toEqual( "0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf" From 008b81134ad50b10b49cdcb0eb03da9610b6744c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Fri, 21 Oct 2022 12:02:38 +0200 Subject: [PATCH 07/35] Update typo --- src/dapp.ts | 4 ++-- src/forward/forwardHandler.ts | 4 ++-- tests/forward/forwardHandler.test.ts | 20 +++++++++----------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/dapp.ts b/src/dapp.ts index adf729b..d17ee94 100644 --- a/src/dapp.ts +++ b/src/dapp.ts @@ -9,7 +9,7 @@ import { } from "./requestConsistency"; import { encodeValue } from "./resultEncoder"; import { ethers } from "ethers"; -import { triggerMultiFowardRequest } from "./forward/forwardHandler"; +import { triggerMultiForwardRequest } from "./forward/forwardHandler"; const start = async () => { const inputFolder = process.env.IEXEC_IN; @@ -83,7 +83,7 @@ const start = async () => { : undefined; if (requestedChainIds) { console.log("Crosschain requested [chains:%s]", requestedChainIds); - const isCrossChainRequestSent = await triggerMultiFowardRequest( + const isCrossChainRequestSent = await triggerMultiForwardRequest( requestedChainIds, oracleId, encodedValue diff --git a/src/forward/forwardHandler.ts b/src/forward/forwardHandler.ts index 9ae7b7c..762e9f1 100644 --- a/src/forward/forwardHandler.ts +++ b/src/forward/forwardHandler.ts @@ -4,7 +4,7 @@ import { getOnChainConfig } from "./forwardEnvironment"; import { getSignedForwardRequest } from "./forwardSigner"; import { postMultiForwardRequest } from "./forwardSender"; -export async function triggerMultiFowardRequest( +export async function triggerMultiForwardRequest( requestedChainIds: number[], oracleId: string, encodedValue: string @@ -56,7 +56,7 @@ export async function triggerMultiFowardRequest( }; console.log( - "Multi foward request ready [request:%s]", + "Multi forward request ready [request:%s]", JSON.stringify(multiForwardRequest) ); diff --git a/tests/forward/forwardHandler.test.ts b/tests/forward/forwardHandler.test.ts index 755dbd6..9f844e6 100644 --- a/tests/forward/forwardHandler.test.ts +++ b/tests/forward/forwardHandler.test.ts @@ -1,6 +1,6 @@ import { Wallet } from "ethers"; -import { triggerMultiFowardRequest } from "../../src/forward/forwardHandler"; -import { getWalletWithProvider } from "../../src/forward/walletLoader"; +import { triggerMultiForwardRequest } from "../../src/forward/forwardHandler"; +import { loadWallet } from "../../src/forward/walletLoader"; import { getSignedForwardRequest } from "../../src/forward/forwardSigner"; import { postMultiForwardRequest } from "../../src/forward/forwardSender"; @@ -8,9 +8,7 @@ jest.mock("../../src/forward/walletLoader"); jest.mock("../../src/forward/forwardSigner"); jest.mock("../../src/forward/forwardSender"); -const getWalletWithProviderMock = getWalletWithProvider as jest.MockedFunction< - typeof getWalletWithProvider ->; +const loadWalletMock = loadWallet as jest.MockedFunction; const getSignedForwardRequestMock = getSignedForwardRequest as jest.MockedFunction< typeof getSignedForwardRequest @@ -37,7 +35,7 @@ describe("Forward handler", () => { test("should not trigger since task ID missing", async () => { process.env.IEXEC_TASK_ID = ""; - const success = await triggerMultiFowardRequest( + const success = await triggerMultiForwardRequest( [CHAIN_ID], ORACLE_ID, VALUE @@ -48,7 +46,7 @@ describe("Forward handler", () => { test("should not trigger since chain not supported", async () => { process.env.IEXEC_TASK_ID = TASK_ID; - const success = await triggerMultiFowardRequest( + const success = await triggerMultiForwardRequest( [12345], // chain not supported ORACLE_ID, VALUE @@ -59,11 +57,11 @@ describe("Forward handler", () => { test("should not trigger since wallet not found", async () => { process.env.IEXEC_TASK_ID = TASK_ID; - getWalletWithProviderMock.mockImplementation(() => { + loadWalletMock.mockImplementation(() => { throw new Error("Wallet error"); }); - const success = await triggerMultiFowardRequest( + const success = await triggerMultiForwardRequest( [CHAIN_ID], ORACLE_ID, VALUE @@ -74,7 +72,7 @@ describe("Forward handler", () => { test("should trigger", async () => { process.env.IEXEC_TASK_ID = TASK_ID; - getWalletWithProviderMock.mockReturnValue(wallet); + loadWalletMock.mockReturnValue(wallet); getSignedForwardRequestMock.mockReturnValue( Promise.resolve({ eip712: { @@ -99,7 +97,7 @@ describe("Forward handler", () => { ); postMultiForwardRequestMock.mockReturnValue(Promise.resolve(true)); - const success = await triggerMultiFowardRequest( + const success = await triggerMultiForwardRequest( [CHAIN_ID], ORACLE_ID, VALUE From c0cc3b6bfc4e1530a17e39006e3e82e1f95f98d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Fri, 21 Oct 2022 12:21:49 +0200 Subject: [PATCH 08/35] Use Record in forward env --- src/forward/forwardEnvironment.ts | 35 +++++++++++++------------------ 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/forward/forwardEnvironment.ts b/src/forward/forwardEnvironment.ts index 36a33ee..88dc29a 100644 --- a/src/forward/forwardEnvironment.ts +++ b/src/forward/forwardEnvironment.ts @@ -1,26 +1,21 @@ export const forwarderApiUrl = "http://localhost:3000"; -const chainIdToOnChainConfig = new Map([ - [ - 5, // Goerli - { - forwarder: "0xc83de370A0D1C99F3D3D9e77bd930520ded81fFA", - oracle: "0x8Ad317241854b1A29A06cE5478e6B92FA09Cd03a", - providerUrl: undefined, //use default provider from ethers - }, - ], - [ - 80001, // Mumbai Polygon - { - forwarder: "0x6843aA5A3a777Ae750DD9d93a9D0fdF99e061b53", - oracle: "0x68bDfa911178f72CEA7BCFd0FeEbbA4cDDE24eCF", - providerUrl: "https://rpc-mumbai.maticvigil.com", - }, - ], -]); - +const chainIdToOnChainConfig: Record = { + // Goerli + 5: { + forwarder: "0xc83de370A0D1C99F3D3D9e77bd930520ded81fFA", + oracle: "0x8Ad317241854b1A29A06cE5478e6B92FA09Cd03a", + providerUrl: undefined, //use default provider from ethers + }, + // Mumbai Polygon + 80001: { + forwarder: "0x6843aA5A3a777Ae750DD9d93a9D0fdF99e061b53", + oracle: "0x68bDfa911178f72CEA7BCFd0FeEbbA4cDDE24eCF", + providerUrl: "https://rpc-mumbai.maticvigil.com", + }, +}; export function getOnChainConfig(chainId: number) { - return chainIdToOnChainConfig.get(chainId); + return chainIdToOnChainConfig[chainId]; } export interface OnChainConfig { From d5a01a57d8d30c93efaa853fa13f81f2d849550f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Fri, 21 Oct 2022 12:30:41 +0200 Subject: [PATCH 09/35] Use same args for tests and runtime --- src/dapp.ts | 5 ++--- tests/dapp.integ.test.ts | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/dapp.ts b/src/dapp.ts index d17ee94..157b8e3 100644 --- a/src/dapp.ts +++ b/src/dapp.ts @@ -74,12 +74,11 @@ const start = async () => { return undefined; } - // `node app.ts bla` (0, 1, 2) - console.log(process.argv); + // Native command line arguments - 0:node, 1:app.ts, 2:arg1 const requestedChainIds = process.argv.length > 2 ? // Parse chainIds, sort them, remove duplicates, cast them from string to number - Array.from(new Set(process.argv[4].split(",").sort()), Number) //TODO: Only works in tests, update to 2 + Array.from(new Set(process.argv[2].split(",").sort()), Number) : undefined; if (requestedChainIds) { console.log("Crosschain requested [chains:%s]", requestedChainIds); diff --git a/tests/dapp.integ.test.ts b/tests/dapp.integ.test.ts index ecde04e..c07e543 100644 --- a/tests/dapp.integ.test.ts +++ b/tests/dapp.integ.test.ts @@ -32,7 +32,7 @@ describe("dapp", () => { test("a full successful dapp IT run without dataset", async () => { dotEnvConfig(); - process.argv.push("5,80001"); // Goerli & Mumbai Polygon + process.argv = ["", "", "5,80001"]; // Goerli & Mumbai Polygon process.env.IEXEC_TASK_ID = "0x0000000000000000000000000000000000000000000000000000000000000abc"; process.env.IEXEC_APP_DEVELOPER_SECRET = From dad0ad334de4e795a7688678923289912acee49b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Tue, 25 Oct 2022 14:12:07 +0200 Subject: [PATCH 10/35] Trigger separate requests & Add forward handler tests --- src/dapp.ts | 4 +- src/forward/forwardEnvironment.ts | 2 +- src/forward/forwardHandler.ts | 92 ++++++++++++---------------- src/forward/forwardSender.ts | 7 ++- src/forward/forwardSigner.ts | 20 +++--- src/forward/oracleContractWrapper.ts | 29 +++++++++ tests/forward/forwardHandler.test.ts | 32 +++------- tests/forward/forwardSigner.test.ts | 77 +++++++++++++++++++++++ 8 files changed, 170 insertions(+), 93 deletions(-) create mode 100644 src/forward/oracleContractWrapper.ts create mode 100644 tests/forward/forwardSigner.test.ts diff --git a/src/dapp.ts b/src/dapp.ts index 157b8e3..a719774 100644 --- a/src/dapp.ts +++ b/src/dapp.ts @@ -9,7 +9,7 @@ import { } from "./requestConsistency"; import { encodeValue } from "./resultEncoder"; import { ethers } from "ethers"; -import { triggerMultiForwardRequest } from "./forward/forwardHandler"; +import { triggerForwardRequests } from "./forward/forwardHandler"; const start = async () => { const inputFolder = process.env.IEXEC_IN; @@ -82,7 +82,7 @@ const start = async () => { : undefined; if (requestedChainIds) { console.log("Crosschain requested [chains:%s]", requestedChainIds); - const isCrossChainRequestSent = await triggerMultiForwardRequest( + const isCrossChainRequestSent = await triggerForwardRequests( requestedChainIds, oracleId, encodedValue diff --git a/src/forward/forwardEnvironment.ts b/src/forward/forwardEnvironment.ts index 88dc29a..14cd2e6 100644 --- a/src/forward/forwardEnvironment.ts +++ b/src/forward/forwardEnvironment.ts @@ -1,4 +1,4 @@ -export const forwarderApiUrl = "http://localhost:3000"; +export const forwarderApiUrl = "http://localhost:5000"; const chainIdToOnChainConfig: Record = { // Goerli diff --git a/src/forward/forwardHandler.ts b/src/forward/forwardHandler.ts index 762e9f1..fc84fe0 100644 --- a/src/forward/forwardHandler.ts +++ b/src/forward/forwardHandler.ts @@ -4,7 +4,7 @@ import { getOnChainConfig } from "./forwardEnvironment"; import { getSignedForwardRequest } from "./forwardSigner"; import { postMultiForwardRequest } from "./forwardSender"; -export async function triggerMultiForwardRequest( +export async function triggerForwardRequests( requestedChainIds: number[], oracleId: string, encodedValue: string @@ -15,58 +15,44 @@ export async function triggerMultiForwardRequest( return false; } - const chainIds = requestedChainIds.filter(isSupportedChain); - const signedForwardRequests = []; - - for (const chainId of chainIds) { - const onChainConfig = getOnChainConfig(chainId); - if (!onChainConfig) { - // already checked, should not happen - console.error("Chain not supported [chainId:%s]", chainId); - continue; - } - - let wallet: Wallet; - try { - // validate args or exit before going further - wallet = loadWallet(process.env.IEXEC_APP_DEVELOPER_SECRET); - } catch (e) { - console.error("Failed to load ClassicOracle from encoded args [e:%s]", e); - continue; - } - - const signedForwardRequest = await getSignedForwardRequest( - chainId, - wallet, - taskId, - oracleId, - encodedValue, - onChainConfig - ); - - signedForwardRequests.push(signedForwardRequest); - } - - if (signedForwardRequests.length == 0) { - return false; - } - - const multiForwardRequest = { - requests: signedForwardRequests, - }; - - console.log( - "Multi forward request ready [request:%s]", - JSON.stringify(multiForwardRequest) + const successes = await Promise.all( + requestedChainIds.map(async (chainId) => { + const onChainConfig = getOnChainConfig(chainId); + if (!onChainConfig) { + console.error("Chain not supported [chainId:%s]", chainId); + return false; + } + + let wallet: Wallet; + try { + // validate args or exit before going further + wallet = loadWallet(process.env.IEXEC_APP_DEVELOPER_SECRET); + } catch (e) { + console.error( + "Failed to load ClassicOracle from encoded args [e:%s]", + e + ); + return false; + } + + const signedForwardRequest = await getSignedForwardRequest( + chainId, + wallet, + taskId, + oracleId, + encodedValue, + onChainConfig + ); + + return await postMultiForwardRequest( + signedForwardRequest, + oracleId, + taskId + ); + }) ); - return await postMultiForwardRequest(multiForwardRequest, oracleId, taskId); -} - -function isSupportedChain(chainId: number) { - if (getOnChainConfig(chainId) != undefined) { - return true; - } - console.error("Chain not supported [chainId:%s]", chainId); - return false; + return ( + successes.filter((success) => success).length == requestedChainIds.length + ); } diff --git a/src/forward/forwardSender.ts b/src/forward/forwardSender.ts index 6b9c31c..66b9802 100644 --- a/src/forward/forwardSender.ts +++ b/src/forward/forwardSender.ts @@ -2,13 +2,14 @@ import fetch from "node-fetch"; import { forwarderApiUrl } from "./forwardEnvironment"; export async function postMultiForwardRequest( - multiForwardRequest: any, + signedForwardRequest: any, oracleId: string, taskId: string ): Promise { + console.log(signedForwardRequest); const response = await fetch(forwarderApiUrl + "/forward", { method: "post", - body: JSON.stringify(multiForwardRequest), + body: JSON.stringify(signedForwardRequest), headers: { "Content-Type": "application/json" }, }); @@ -25,7 +26,7 @@ export async function postMultiForwardRequest( oracleId, taskId, response.status, - await response.json().then((aa) => console.log(aa.message)) + await response.json().then((body) => console.log(body.message)) ); return false; } diff --git a/src/forward/forwardSigner.ts b/src/forward/forwardSigner.ts index 75e0888..6d487ca 100644 --- a/src/forward/forwardSigner.ts +++ b/src/forward/forwardSigner.ts @@ -1,6 +1,6 @@ import { ethers } from "ethers"; -import { ClassicOracle__factory } from "@iexec/generic-oracle-contracts/typechain"; import { OnChainConfig } from "./forwardEnvironment"; +import { ReceiveResultContractFunction } from "./oracleContractWrapper"; export async function getSignedForwardRequest( chainId: number, @@ -38,33 +38,29 @@ export async function getSignedForwardRequest( provider = ethers.getDefaultProvider(chainId); } - const classicOracle = new ClassicOracle__factory() - .attach(oracleAddress) - .connect(provider); + const receiveResult = new ReceiveResultContractFunction( + oracleAddress, + provider + ); const forwardRequest = { from: reporterAddress, to: oracleAddress, value: "0", gas: ( - await classicOracle.estimateGas.receiveResult(taskId, encodedValue, { - from: reporterAddress, - }) + await receiveResult.getGasEstimate(taskId, encodedValue, reporterAddress) ).toString(), salt: ethers.utils.keccak256( ethers.utils.toUtf8Bytes(Math.random().toString()) ), - data: classicOracle.interface.encodeFunctionData("receiveResult", [ - taskId, - encodedValue, - ]), + data: receiveResult.getData(taskId, encodedValue), }; const signature = await wallet._signTypedData(domain, types, forwardRequest); const signedForwardRequest = { eip712: { - types: types.ForwardRequest, + types: types, domain: domain, message: forwardRequest, }, diff --git a/src/forward/oracleContractWrapper.ts b/src/forward/oracleContractWrapper.ts new file mode 100644 index 0000000..4b04c7b --- /dev/null +++ b/src/forward/oracleContractWrapper.ts @@ -0,0 +1,29 @@ +import { ethers } from "ethers"; +import { ClassicOracle__factory } from "@iexec/generic-oracle-contracts/typechain"; + +export class ReceiveResultContractFunction { + oracleContract; //public for tests + + constructor(oracleAddress: string, provider: ethers.providers.BaseProvider) { + this.oracleContract = new ClassicOracle__factory() + .attach(oracleAddress) + .connect(provider); + } + + getGasEstimate( + taskId: string, + encodedValue: string, + reporterAddress: string + ) { + return this.oracleContract.estimateGas.receiveResult(taskId, encodedValue, { + from: reporterAddress, + }); + } + + getData(taskId: string, encodedValue: string) { + return this.oracleContract.interface.encodeFunctionData("receiveResult", [ + taskId, + encodedValue, + ]); + } +} diff --git a/tests/forward/forwardHandler.test.ts b/tests/forward/forwardHandler.test.ts index 9f844e6..a42d550 100644 --- a/tests/forward/forwardHandler.test.ts +++ b/tests/forward/forwardHandler.test.ts @@ -1,5 +1,5 @@ import { Wallet } from "ethers"; -import { triggerMultiForwardRequest } from "../../src/forward/forwardHandler"; +import { triggerForwardRequests } from "../../src/forward/forwardHandler"; import { loadWallet } from "../../src/forward/walletLoader"; import { getSignedForwardRequest } from "../../src/forward/forwardSigner"; import { postMultiForwardRequest } from "../../src/forward/forwardSender"; @@ -35,23 +35,19 @@ describe("Forward handler", () => { test("should not trigger since task ID missing", async () => { process.env.IEXEC_TASK_ID = ""; - const success = await triggerMultiForwardRequest( - [CHAIN_ID], - ORACLE_ID, - VALUE - ); - expect(success).toBeFalsy(); + const success = await triggerForwardRequests([CHAIN_ID], ORACLE_ID, VALUE); + expect(success).toBe(false); }); test("should not trigger since chain not supported", async () => { process.env.IEXEC_TASK_ID = TASK_ID; - const success = await triggerMultiForwardRequest( + const success = await triggerForwardRequests( [12345], // chain not supported ORACLE_ID, VALUE ); - expect(success).toBeFalsy(); + expect(success).toBe(false); }); test("should not trigger since wallet not found", async () => { @@ -61,12 +57,8 @@ describe("Forward handler", () => { throw new Error("Wallet error"); }); - const success = await triggerMultiForwardRequest( - [CHAIN_ID], - ORACLE_ID, - VALUE - ); - expect(success).toBeFalsy(); + const success = await triggerForwardRequests([CHAIN_ID], ORACLE_ID, VALUE); + expect(success).toBe(false); }); test("should trigger", async () => { @@ -76,7 +68,7 @@ describe("Forward handler", () => { getSignedForwardRequestMock.mockReturnValue( Promise.resolve({ eip712: { - types: [], + types: { ForwardRequest: [] }, domain: { name: "string", version: "string", @@ -97,11 +89,7 @@ describe("Forward handler", () => { ); postMultiForwardRequestMock.mockReturnValue(Promise.resolve(true)); - const success = await triggerMultiForwardRequest( - [CHAIN_ID], - ORACLE_ID, - VALUE - ); - expect(success).toBeTruthy(); + const success = await triggerForwardRequests([CHAIN_ID], ORACLE_ID, VALUE); + expect(success).toBe(true); }); }); diff --git a/tests/forward/forwardSigner.test.ts b/tests/forward/forwardSigner.test.ts new file mode 100644 index 0000000..b1b3a9e --- /dev/null +++ b/tests/forward/forwardSigner.test.ts @@ -0,0 +1,77 @@ +import { ClassicOracle } from "@iexec/generic-oracle-contracts/typechain"; +import { Wallet, BigNumber } from "ethers"; +import { getSignedForwardRequest } from "../../src/forward/forwardSigner"; +import { ReceiveResultContractFunction } from "../../src/forward/oracleContractWrapper"; + +jest.mock("../../src/forward/oracleContractWrapper"); + +const TASK_ID = + "0x0000000000000000000000000000000000000000000000000000000000000abc"; +const CHAIN_ID = 5; +const ORACLE_ID = "0x1"; +const VALUE = "0x2"; +const wallet = new Wallet( + "0x0000000000000000000000000000000000000000000000000000000000000001" +); + +describe("Forward signer", () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + test("should", async () => { + const receiveResultMock = { + oracleContract: {} as ClassicOracle, + getGasEstimate: () => Promise.resolve(BigNumber.from("100000")), + getData: () => "0xabcd", + } as ReceiveResultContractFunction; + + jest + .mocked(ReceiveResultContractFunction) + .mockImplementation(() => receiveResultMock); + + jest.spyOn(Math, "random").mockReturnValue(123456789); + + const signedRequest = await getSignedForwardRequest( + CHAIN_ID, + wallet, + TASK_ID, + ORACLE_ID, + VALUE, + { + forwarder: "0x0000000000000000000000000000000000000001", + oracle: "0x0000000000000000000000000000000000000002", + providerUrl: undefined, + } + ); + expect(signedRequest).toEqual({ + eip712: { + types: { + ForwardRequest: [ + { name: "from", type: "address" }, + { name: "to", type: "address" }, + { name: "value", type: "uint256" }, + { name: "gas", type: "uint256" }, + { name: "salt", type: "bytes32" }, + { name: "data", type: "bytes" }, + ], + }, + domain: { + name: "SaltyForwarder", + version: "0.0.1", + chainId: "5", + verifyingContract: "0x0000000000000000000000000000000000000001", + }, + message: { + from: "0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf", + to: "0x0000000000000000000000000000000000000002", + value: "0", + gas: "100000", + salt: "0x2a359feeb8e488a1af2c03b908b3ed7990400555db73e1421181d97cac004d48", + data: "0xabcd", + }, + }, + sign: "0x85457e9405fbe9a75a8604f4bd678ce8a5276215ea78cad77376b406f7a325f67efb764efd0d2d4a01b35a1eb2e47e90625a1f7056e5c3853068bca64e1b51e21c", + }); + }); +}); From 38b99e9e3b67d952e4e2b6e5c2326a202b6e6f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Wed, 26 Oct 2022 10:39:34 +0200 Subject: [PATCH 11/35] Use live dev Forwarder API --- src/forward/forwardEnvironment.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/forward/forwardEnvironment.ts b/src/forward/forwardEnvironment.ts index 14cd2e6..4499cb3 100644 --- a/src/forward/forwardEnvironment.ts +++ b/src/forward/forwardEnvironment.ts @@ -1,4 +1,7 @@ -export const forwarderApiUrl = "http://localhost:5000"; +// localhost +//export const forwarderApiUrl = "http://localhost:5000"; +// dev.iex.ec +export const forwarderApiUrl = "https://forwarder.dev-oracle-factory.iex.ec"; const chainIdToOnChainConfig: Record = { // Goerli From d0b9055079305abd996ef0d34509c0fe0c9dfd73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Wed, 26 Oct 2022 16:46:39 +0200 Subject: [PATCH 12/35] Bundle in /app of Dockerfile --- docker/Dockerfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 5a2b810..beff950 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,12 +1,11 @@ FROM node:14-alpine3.10 AS builder -WORKDIR /app COPY package*.json tsconfig.json ./ -COPY src/ . +COPY src/ /src/ RUN npm ci # Future note: Run unit tests automatically RUN npm run build # Copy generated *.js to /app so we can use them -RUN cp -R app/* ./ +RUN cp -R app/src/* ./app FROM node:14-alpine3.10 WORKDIR /app From c8198241b53c99ced92411022fce01923d0007d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Fri, 28 Oct 2022 11:42:12 +0200 Subject: [PATCH 13/35] Update TEE dapp build/deployment samples --- Readme.md | 5 +++++ deployed.json | 4 ++-- docker/sconify.sh | 2 +- iexec.json | 24 ++++++++++++------------ package.json | 2 +- 5 files changed, 21 insertions(+), 16 deletions(-) diff --git a/Readme.md b/Readme.md index 1c46ad8..592a591 100644 --- a/Readme.md +++ b/Readme.md @@ -85,6 +85,10 @@ iexec app deploy --chain bellecour ``` iexec-core-cli app push-owner-secret --secret=$MY_SECRETS --sms= --wallet-path=/tmp/wallet.json --wallet-password ``` +or +``` +iexec app push-secret --secret-value $MY_SECRETS --chain bellecour +``` ``` iexec order sign --app --chain bellecour @@ -97,6 +101,7 @@ iexec order sign --request --chain bellecour ``` ``` iexec orderbook workerpool --tag tee <0xworkerpool> --chain bellecour +iexec orderbook workerpool --tag tee <0xworkerpool> --chain bellecour --raw | jq -r .workerpoolOrders[0].orderHash ``` ``` iexec order fill --chain bellecour --workerpool diff --git a/deployed.json b/deployed.json index 9d0a762..abca2e4 100644 --- a/deployed.json +++ b/deployed.json @@ -1,5 +1,5 @@ { "app": { - "134": "0xa7E233a6648d77872005E7C4689B63DFbbDd8857" + "134": "0xdce234BF640235549D4D95C4764B1863FfDa7cF4" } -} +} \ No newline at end of file diff --git a/docker/sconify.sh b/docker/sconify.sh index d7374a8..fbd2373 100644 --- a/docker/sconify.sh +++ b/docker/sconify.sh @@ -9,5 +9,5 @@ ARGS=$(sed -e "s'\${IMG_FROM}'${IMG_FROM}'" -e "s'\${IMG_TO}'${IMG_TO}'" sconify echo $ARGS /bin/bash -c "docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \ - registry.scontain.com:5050/sconecuratedimages/iexec-sconify-image:5.3.6 \ + registry.scontain.com:5050/scone-production/iexec-sconify-image:5.3.15 \ sconify_iexec $ARGS" diff --git a/iexec.json b/iexec.json index c5bd067..1e344e9 100644 --- a/iexec.json +++ b/iexec.json @@ -17,26 +17,26 @@ }, "app": { "owner": "0x15Bd06807eF0F2284a9C5baeAA2EF4d5a88eB72A", - "name": "generic-oracle-dapp-2", + "name": "generic-oracle-dapp-xc", "type": "DOCKER", - "multiaddr": "docker.io/iexechub/generic-oracle-dapp:feature-99feb9ab-sconify-5.3.15-debug", - "checksum": "0xd0d4c6828115d01ec5f4fd8350bd485f9f4b904050fdce7be77ab6655af11df4", + "multiaddr": "docker.io/iexechub/generic-oracle-dapp:feature-d0b90550-sconify-5.3.15-production", + "checksum": "0xf7485b254816519e4e49aaaa66e31a99877f3fd19feb2dfe880513b44bfe4fde", "mrenclave": { "provider": "SCONE", "version": "v5", "entrypoint": "node /app/app.js", "heapSize": 1073741824, - "fingerprint": "820fef5ec06be4bf1b49e463a0f4a6531254807d2d34b33c8e454f5ac46c8b2c" + "fingerprint": "0e485080518310768ebab8eff3600bb188fe9b669ce19092dde984ab7c900fad" } }, "order": { "requestorder": { - "app": "0xa7E233a6648d77872005E7C4689B63DFbbDd8857", + "app": "0xdce234BF640235549D4D95C4764B1863FfDa7cF4", "appmaxprice": "0", "dataset": "0x0000000000000000000000000000000000000000", "datasetmaxprice": "0", - "workerpool": "0x09bc1b06A695Fa9d2A98AC336331872EA81F307D", - "workerpoolmaxprice": "10000000", + "workerpool": "0xEb14Dc854A8873e419183c81a657d025EC70276b", + "workerpoolmaxprice": "0", "volume": "1", "category": "0", "trust": "0", @@ -44,9 +44,9 @@ "beneficiary": "0x15Bd06807eF0F2284a9C5baeAA2EF4d5a88eB72A", "callback": "0x8ecEDdd1377E52d23A46E2bd3dF0aFE35B526D5F", "params": { - "iexec_args": "", + "iexec_args": "5,80001", "iexec_input_files": [ - "https://raw.githubusercontent.com/iExecBlockchainComputing/generic-oracle-dapp/feature/goerli-crosschain/tests/test_files/input_file_no_dataset.json" + "https://raw.githubusercontent.com/iExecBlockchainComputing/generic-oracle-dapp/feature/metatxs-to-forwarder-api/tests/test_files/input_file_no_dataset.json" ], "iexec_result_encryption": false, "iexec_developer_logger": true @@ -54,12 +54,12 @@ "requester": "0x15Bd06807eF0F2284a9C5baeAA2EF4d5a88eB72A" }, "apporder": { - "app": "0xa7E233a6648d77872005E7C4689B63DFbbDd8857", + "app": "0xdce234BF640235549D4D95C4764B1863FfDa7cF4", "appprice": "0", - "volume": "10", + "volume": "1000", "tag": "0x0000000000000000000000000000000000000000000000000000000000000001", "datasetrestrict": "0x0000000000000000000000000000000000000000", - "workerpoolrestrict": "0x0000000000000000000000000000000000000000", + "workerpoolrestrict": "0xEb14Dc854A8873e419183c81a657d025EC70276b", "requesterrestrict": "0x0000000000000000000000000000000000000000" } } diff --git a/package.json b/package.json index 72ae5cf..e18e16e 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "test": "jest --coverage", "itest": "jest --coverage tests/dapp.integ.test.ts", "build": "tsc", - "scone": "bash sconify.sh" + "scone": "bash docker/sconify.sh" }, "repository": { "type": "git", From 169f9a6baf36d30aab8ddc7f5bfe3b531b5d8b4a 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: Fri, 28 Oct 2022 12:33:04 +0200 Subject: [PATCH 14/35] Generate test report on CI build (#16) * Init tests on CI * Ignore integration test --- Jenkinsfile | 12 ++++++++++++ package.json | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index c6d0777..7fa63cd 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -1,5 +1,17 @@ @Library('global-jenkins-library@2.0.0') _ +docker.image('node:16-alpine').inside { + stage('Test') { + checkout scm + sh ''' + npm ci + npm run build + npm test + ''' + archiveArtifacts artifacts: 'coverage/' + } +} + appName = 'generic-oracle-dapp' buildInfo = getBuildInfo() diff --git a/package.json b/package.json index e18e16e..f1b0d56 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "scripts": { "lint": "eslint --ext .js,.ts .", "format": "prettier --ignore-path .gitignore --write \"**/*.+(js|ts|json)\"", - "test": "jest --coverage", + "test": "jest --coverage --testPathIgnorePatterns=\"dapp.integ\"", "itest": "jest --coverage tests/dapp.integ.test.ts", "build": "tsc", "scone": "bash docker/sconify.sh" From 02cacc48ae064436d45ca14fd895fd76d3649ee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Fri, 28 Oct 2022 17:26:00 +0200 Subject: [PATCH 15/35] Add unit tests on dapp & forward sender --- Jenkinsfile | 2 +- package.json | 3 +- tests/dapp.integ.test.ts | 22 +++------------ tests/dapp.test.ts | 14 ++++++++- tests/forward/forwardSender.test.ts | 44 +++++++++++++++++++++++++++++ tests/utils.ts | 15 ++++++++++ 6 files changed, 79 insertions(+), 21 deletions(-) create mode 100644 tests/forward/forwardSender.test.ts create mode 100644 tests/utils.ts diff --git a/Jenkinsfile b/Jenkinsfile index 7fa63cd..bb06a87 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -6,7 +6,7 @@ docker.image('node:16-alpine').inside { sh ''' npm ci npm run build - npm test + npm ci-test ''' archiveArtifacts artifacts: 'coverage/' } diff --git a/package.json b/package.json index f1b0d56..9c72795 100644 --- a/package.json +++ b/package.json @@ -36,8 +36,9 @@ "scripts": { "lint": "eslint --ext .js,.ts .", "format": "prettier --ignore-path .gitignore --write \"**/*.+(js|ts|json)\"", - "test": "jest --coverage --testPathIgnorePatterns=\"dapp.integ\"", + "test": "jest --coverage", "itest": "jest --coverage tests/dapp.integ.test.ts", + "ci-test": "jest --coverage --testPathIgnorePatterns=\"dapp.integ\"", "build": "tsc", "scone": "bash docker/sconify.sh" }, diff --git a/tests/dapp.integ.test.ts b/tests/dapp.integ.test.ts index c07e543..9350104 100644 --- a/tests/dapp.integ.test.ts +++ b/tests/dapp.integ.test.ts @@ -1,5 +1,6 @@ import { config as dotEnvConfig } from "dotenv"; import Dapp from "../src/dapp"; +import { buildAppSecret } from "./utils"; const somePrivateKey = "0x0000000000000000000000000000000000000000000000000000000000000001"; @@ -35,8 +36,9 @@ describe("dapp", () => { process.argv = ["", "", "5,80001"]; // Goerli & Mumbai Polygon process.env.IEXEC_TASK_ID = "0x0000000000000000000000000000000000000000000000000000000000000abc"; - process.env.IEXEC_APP_DEVELOPER_SECRET = - buildAppSecretWithValidInfuraProcessEnv(process.env.TARGET_PRIVATE_KEY); + process.env.IEXEC_APP_DEVELOPER_SECRET = buildAppSecret( + process.env.TARGET_PRIVATE_KEY + ); process.env.IEXEC_INPUT_FILES_NUMBER = "1"; process.env.IEXEC_INPUT_FILES_FOLDER = "./tests/test_files"; process.env.IEXEC_INPUT_FILE_NAME_1 = "input_file_no_dataset.json"; @@ -54,19 +56,3 @@ describe("dapp", () => { */ }, 60000); //sending tx takes some time }); - -function buildAppSecretWithValidInfuraProcessEnv( - targetPrivateKey: string | undefined -) { - dotEnvConfig(); - const infuraProjectId = process.env.INFURA_PROJECT_ID; - const infuraProjectSecret = process.env.INFURA_PROJECT_SECRET; - const appDeveloperSecretJsonString = JSON.stringify({ - infuraProjectId: infuraProjectId, - infuraProjectSecret: infuraProjectSecret, - targetPrivateKey: targetPrivateKey, - }); - const buff = Buffer.from(appDeveloperSecretJsonString, "utf-8"); - const encodedAppDeveloperSecret = buff.toString("base64"); - return encodedAppDeveloperSecret; -} diff --git a/tests/dapp.test.ts b/tests/dapp.test.ts index 65b839c..dcd9855 100644 --- a/tests/dapp.test.ts +++ b/tests/dapp.test.ts @@ -1,14 +1,17 @@ import fetch from "node-fetch"; import Dapp from "../src/dapp"; +import { buildAppSecret } from "./utils"; +import { triggerForwardRequests } from "../src/forward/forwardHandler"; jest.mock("node-fetch"); +jest.mock("../src/forward/forwardHandler"); afterEach(() => { jest.resetAllMocks(); }); describe("dapp", () => { - test.skip("a full successful dapp run", async () => { + test("a full successful dapp run", async () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any (fetch as any).mockImplementation(async () => ({ json: () => @@ -22,6 +25,15 @@ describe("dapp", () => { get: () => "Thu, 10 Jun 2021 09:58:20 GMT", }, })); + + jest.mocked(triggerForwardRequests).mockReturnValue(Promise.resolve(true)); + + process.argv = ["", "", "5,80001"]; // Goerli & Mumbai Polygon + process.env.IEXEC_TASK_ID = + "0x0000000000000000000000000000000000000000000000000000000000000abc"; + process.env.IEXEC_APP_DEVELOPER_SECRET = buildAppSecret( + "0x0000000000000000000000000000000000000000000000000000000000000001" + ); process.env.IEXEC_INPUT_FILES_FOLDER = "./tests/test_files"; process.env.IEXEC_INPUT_FILE_NAME_1 = "input_file.json"; process.env.IEXEC_OUT = "./tests/test_out"; diff --git a/tests/forward/forwardSender.test.ts b/tests/forward/forwardSender.test.ts new file mode 100644 index 0000000..34950fb --- /dev/null +++ b/tests/forward/forwardSender.test.ts @@ -0,0 +1,44 @@ +import fetch from "node-fetch"; +import { postMultiForwardRequest } from "../../src/forward/forwardSender"; +import { forwarderApiUrl } from "../../src/forward/forwardEnvironment"; + +jest.mock("node-fetch"); + +const TASK_ID = + "0x0000000000000000000000000000000000000000000000000000000000000abc"; +const ORACLE_ID = "0x1"; + +describe("Forward signer", () => { + beforeEach(() => { + jest.resetAllMocks(); + }); + + test("should successfully forward", async () => { + (fetch as any).mockImplementation(async () => ({ + ok: true, + })); + + await postMultiForwardRequest({ some: "request" }, ORACLE_ID, TASK_ID); + + expect(fetch).toHaveBeenCalledWith(forwarderApiUrl + "/forward", { + method: "post", + body: '{"some":"request"}', + headers: { "Content-Type": "application/json" }, + }); + }); + + test("should not forward", async () => { + (fetch as any).mockImplementation(async () => ({ + ok: false, + status: 500, + json: () => + Promise.resolve({ + message: "error message", + }), + })); + + expect( + await postMultiForwardRequest({ some: "request" }, ORACLE_ID, TASK_ID) + ).toBe(false); + }); +}); diff --git a/tests/utils.ts b/tests/utils.ts new file mode 100644 index 0000000..c199523 --- /dev/null +++ b/tests/utils.ts @@ -0,0 +1,15 @@ +import { config as dotEnvConfig } from "dotenv"; + +export function buildAppSecret(targetPrivateKey: string | undefined) { + dotEnvConfig(); + const infuraProjectId = process.env.INFURA_PROJECT_ID; + const infuraProjectSecret = process.env.INFURA_PROJECT_SECRET; + const appDeveloperSecretJsonString = JSON.stringify({ + infuraProjectId: infuraProjectId, + infuraProjectSecret: infuraProjectSecret, + targetPrivateKey: targetPrivateKey, + }); + const buff = Buffer.from(appDeveloperSecretJsonString, "utf-8"); + const encodedAppDeveloperSecret = buff.toString("base64"); + return encodedAppDeveloperSecret; +} From 1b5bc94c294a1f795aaea96dd038a083d8d73cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Fri, 28 Oct 2022 17:30:49 +0200 Subject: [PATCH 16/35] Update ci-test cmd in Jenkinsfile --- Jenkinsfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Jenkinsfile b/Jenkinsfile index bb06a87..226e741 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -6,7 +6,7 @@ docker.image('node:16-alpine').inside { sh ''' npm ci npm run build - npm ci-test + npm run ci-test ''' archiveArtifacts artifacts: 'coverage/' } From 65c3ee10c194019b1988adc225b7d085fe1d9ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Fri, 28 Oct 2022 17:42:38 +0200 Subject: [PATCH 17/35] Skip success sender test --- tests/forward/forwardSender.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/forward/forwardSender.test.ts b/tests/forward/forwardSender.test.ts index 34950fb..88bfc6b 100644 --- a/tests/forward/forwardSender.test.ts +++ b/tests/forward/forwardSender.test.ts @@ -13,7 +13,8 @@ describe("Forward signer", () => { jest.resetAllMocks(); }); - test("should successfully forward", async () => { + //TODO: Unskip + test.skip("should successfully forward", async () => { (fetch as any).mockImplementation(async () => ({ ok: true, })); From 85fdabd8c9b505b0d894854cc9c486d7a1834284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Fri, 28 Oct 2022 17:53:27 +0200 Subject: [PATCH 18/35] No build before ci-test --- Jenkinsfile | 1 - tests/forward/forwardSender.test.ts | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Jenkinsfile b/Jenkinsfile index 226e741..1a46604 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,7 +5,6 @@ docker.image('node:16-alpine').inside { checkout scm sh ''' npm ci - npm run build npm run ci-test ''' archiveArtifacts artifacts: 'coverage/' diff --git a/tests/forward/forwardSender.test.ts b/tests/forward/forwardSender.test.ts index 88bfc6b..34950fb 100644 --- a/tests/forward/forwardSender.test.ts +++ b/tests/forward/forwardSender.test.ts @@ -13,8 +13,7 @@ describe("Forward signer", () => { jest.resetAllMocks(); }); - //TODO: Unskip - test.skip("should successfully forward", async () => { + test("should successfully forward", async () => { (fetch as any).mockImplementation(async () => ({ ok: true, })); From 9849a30be1741280a77e82997e41287b389549d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Wed, 2 Nov 2022 17:18:45 +0100 Subject: [PATCH 19/35] Update logs --- deployed.json | 2 +- src/dapp.ts | 16 ++++++---- src/forward/forwardHandler.ts | 47 ++++++++++++++++------------ src/forward/forwardSender.ts | 12 ++++--- src/forward/forwardSigner.ts | 4 +-- src/forward/walletLoader.ts | 5 ++- tests/forward/forwardHandler.test.ts | 12 +++---- tests/forward/forwardSender.test.ts | 6 ++-- tests/forward/forwardSigner.test.ts | 4 +-- 9 files changed, 62 insertions(+), 46 deletions(-) diff --git a/deployed.json b/deployed.json index abca2e4..c3c10d4 100644 --- a/deployed.json +++ b/deployed.json @@ -2,4 +2,4 @@ "app": { "134": "0xdce234BF640235549D4D95C4764B1863FfDa7cF4" } -} \ No newline at end of file +} diff --git a/src/dapp.ts b/src/dapp.ts index a719774..3da2315 100644 --- a/src/dapp.ts +++ b/src/dapp.ts @@ -81,17 +81,21 @@ const start = async () => { Array.from(new Set(process.argv[2].split(",").sort()), Number) : undefined; if (requestedChainIds) { - console.log("Crosschain requested [chains:%s]", requestedChainIds); - const isCrossChainRequestSent = await triggerForwardRequests( + console.log( + "User requesting updates on foreign blockchains [chains:%s]", + requestedChainIds + ); + const allForwardRequestsAccepted = await triggerForwardRequests( requestedChainIds, oracleId, encodedValue ); // Status is logged for information purpose only (app must go on on failure) - console.log( - "isCrossChainRequestSent status [status:%s]", - isCrossChainRequestSent - ); + if (allForwardRequestsAccepted) { + console.log("All forward requests accepted by Forwarder API"); + } else { + console.error("At least one forward request rejected by Forwarder API"); + } } return encodedValue; }; diff --git a/src/forward/forwardHandler.ts b/src/forward/forwardHandler.ts index fc84fe0..9231cf1 100644 --- a/src/forward/forwardHandler.ts +++ b/src/forward/forwardHandler.ts @@ -1,8 +1,8 @@ import { loadWallet } from "./walletLoader"; import { Wallet } from "ethers"; import { getOnChainConfig } from "./forwardEnvironment"; -import { getSignedForwardRequest } from "./forwardSigner"; -import { postMultiForwardRequest } from "./forwardSender"; +import { signForwardRequest } from "./forwardSigner"; +import { postForwardRequest } from "./forwardSender"; export async function triggerForwardRequests( requestedChainIds: number[], @@ -11,7 +11,25 @@ export async function triggerForwardRequests( ) { const taskId = process.env.IEXEC_TASK_ID; if (!taskId) { - console.error("[IEXEC] IEXEC_TASK_ID is missing"); + console.error( + "`IEXEC_TASK_ID` environnement variable is missing [oracleId:%s, requestedChainIds:%s]", + oracleId, + requestedChainIds + ); + return false; + } + + let wallet: Wallet; + try { + // validate args or exit before going further + wallet = loadWallet(process.env.IEXEC_APP_DEVELOPER_SECRET); + } catch (error) { + console.error( + "Failed to load wallet from `IEXEC_APP_DEVELOPER_SECRET` [oracleId:%s, taskId:%s, error:%s]", + oracleId, + taskId, + error + ); return false; } @@ -19,23 +37,16 @@ export async function triggerForwardRequests( requestedChainIds.map(async (chainId) => { const onChainConfig = getOnChainConfig(chainId); if (!onChainConfig) { - console.error("Chain not supported [chainId:%s]", chainId); - return false; - } - - let wallet: Wallet; - try { - // validate args or exit before going further - wallet = loadWallet(process.env.IEXEC_APP_DEVELOPER_SECRET); - } catch (e) { console.error( - "Failed to load ClassicOracle from encoded args [e:%s]", - e + "Foreign blockchain requested is not supported [chainId:%s, oracleId:%s, taskId:%s]", + chainId, + oracleId, + taskId ); return false; } - const signedForwardRequest = await getSignedForwardRequest( + const signedForwardRequest = await signForwardRequest( chainId, wallet, taskId, @@ -44,11 +55,7 @@ export async function triggerForwardRequests( onChainConfig ); - return await postMultiForwardRequest( - signedForwardRequest, - oracleId, - taskId - ); + return await postForwardRequest(signedForwardRequest, oracleId, taskId); }) ); diff --git a/src/forward/forwardSender.ts b/src/forward/forwardSender.ts index 66b9802..a3e0493 100644 --- a/src/forward/forwardSender.ts +++ b/src/forward/forwardSender.ts @@ -1,12 +1,11 @@ import fetch from "node-fetch"; import { forwarderApiUrl } from "./forwardEnvironment"; -export async function postMultiForwardRequest( +export async function postForwardRequest( signedForwardRequest: any, oracleId: string, taskId: string ): Promise { - console.log(signedForwardRequest); const response = await fetch(forwarderApiUrl + "/forward", { method: "post", body: JSON.stringify(signedForwardRequest), @@ -15,18 +14,21 @@ export async function postMultiForwardRequest( if (response.ok) { console.log( - "Successful response from Forwarder API [oracleId:%s, taskId:%s]", + "Forward request accepted by Forwarder API [oracleId:%s, taskId:%s]", oracleId, taskId ); return true; } console.error( - "Failure response from Forwarder API [oracleId:%s, taskId:%s, error:%s, data:%s]", + "Forward request rejected by Forwarder API [oracleId:%s, taskId:%s, httpStatus:%s, messageBody:%s]", oracleId, taskId, response.status, - await response.json().then((body) => console.log(body.message)) + await response + .json() + .then((body) => body.message) + .then(console.log) ); return false; } diff --git a/src/forward/forwardSigner.ts b/src/forward/forwardSigner.ts index 6d487ca..5a9bead 100644 --- a/src/forward/forwardSigner.ts +++ b/src/forward/forwardSigner.ts @@ -2,7 +2,7 @@ import { ethers } from "ethers"; import { OnChainConfig } from "./forwardEnvironment"; import { ReceiveResultContractFunction } from "./oracleContractWrapper"; -export async function getSignedForwardRequest( +export async function signForwardRequest( chainId: number, wallet: ethers.Wallet, taskId: string, @@ -67,7 +67,7 @@ export async function getSignedForwardRequest( sign: signature, }; console.log( - "Signed forwardRequest [chainId:%s, oracleId:%s, taskId:%s, encodedValue:%s, signedForwardRequest:%s]", + "Signed forward request [chainId:%s, oracleId:%s, taskId:%s, encodedValue:%s, signedForwardRequest:%s]", chainId, oracleId, taskId, diff --git a/src/forward/walletLoader.ts b/src/forward/walletLoader.ts index aaf7d7f..b4115c4 100644 --- a/src/forward/walletLoader.ts +++ b/src/forward/walletLoader.ts @@ -36,7 +36,10 @@ export function loadWallet(encodedArgs: string | undefined): Wallet { // }); const wallet = new ethers.Wallet(targetPrivateKey); - console.log("Target reporter wallet address: " + wallet.address); + console.log( + "Recovered authorized reporter wallet for foreign oracle contract [address:%s]", + wallet.address + ); return wallet; } diff --git a/tests/forward/forwardHandler.test.ts b/tests/forward/forwardHandler.test.ts index a42d550..3d5c4c2 100644 --- a/tests/forward/forwardHandler.test.ts +++ b/tests/forward/forwardHandler.test.ts @@ -1,8 +1,8 @@ import { Wallet } from "ethers"; import { triggerForwardRequests } from "../../src/forward/forwardHandler"; import { loadWallet } from "../../src/forward/walletLoader"; -import { getSignedForwardRequest } from "../../src/forward/forwardSigner"; -import { postMultiForwardRequest } from "../../src/forward/forwardSender"; +import { signForwardRequest } from "../../src/forward/forwardSigner"; +import { postForwardRequest } from "../../src/forward/forwardSender"; jest.mock("../../src/forward/walletLoader"); jest.mock("../../src/forward/forwardSigner"); @@ -10,12 +10,12 @@ jest.mock("../../src/forward/forwardSender"); const loadWalletMock = loadWallet as jest.MockedFunction; const getSignedForwardRequestMock = - getSignedForwardRequest as jest.MockedFunction< - typeof getSignedForwardRequest + signForwardRequest as jest.MockedFunction< + typeof signForwardRequest >; const postMultiForwardRequestMock = - postMultiForwardRequest as jest.MockedFunction< - typeof postMultiForwardRequest + postForwardRequest as jest.MockedFunction< + typeof postForwardRequest >; const TASK_ID = diff --git a/tests/forward/forwardSender.test.ts b/tests/forward/forwardSender.test.ts index 34950fb..562a678 100644 --- a/tests/forward/forwardSender.test.ts +++ b/tests/forward/forwardSender.test.ts @@ -1,5 +1,5 @@ import fetch from "node-fetch"; -import { postMultiForwardRequest } from "../../src/forward/forwardSender"; +import { postForwardRequest } from "../../src/forward/forwardSender"; import { forwarderApiUrl } from "../../src/forward/forwardEnvironment"; jest.mock("node-fetch"); @@ -18,7 +18,7 @@ describe("Forward signer", () => { ok: true, })); - await postMultiForwardRequest({ some: "request" }, ORACLE_ID, TASK_ID); + await postForwardRequest({ some: "request" }, ORACLE_ID, TASK_ID); expect(fetch).toHaveBeenCalledWith(forwarderApiUrl + "/forward", { method: "post", @@ -38,7 +38,7 @@ describe("Forward signer", () => { })); expect( - await postMultiForwardRequest({ some: "request" }, ORACLE_ID, TASK_ID) + await postForwardRequest({ some: "request" }, ORACLE_ID, TASK_ID) ).toBe(false); }); }); diff --git a/tests/forward/forwardSigner.test.ts b/tests/forward/forwardSigner.test.ts index b1b3a9e..c05a458 100644 --- a/tests/forward/forwardSigner.test.ts +++ b/tests/forward/forwardSigner.test.ts @@ -1,6 +1,6 @@ import { ClassicOracle } from "@iexec/generic-oracle-contracts/typechain"; import { Wallet, BigNumber } from "ethers"; -import { getSignedForwardRequest } from "../../src/forward/forwardSigner"; +import { signForwardRequest } from "../../src/forward/forwardSigner"; import { ReceiveResultContractFunction } from "../../src/forward/oracleContractWrapper"; jest.mock("../../src/forward/oracleContractWrapper"); @@ -32,7 +32,7 @@ describe("Forward signer", () => { jest.spyOn(Math, "random").mockReturnValue(123456789); - const signedRequest = await getSignedForwardRequest( + const signedRequest = await signForwardRequest( CHAIN_ID, wallet, TASK_ID, From 564dfccd9a6c8f79e26d57d26b77c164707f3888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Wed, 2 Nov 2022 17:25:43 +0100 Subject: [PATCH 20/35] Move past get provider method --- src/forward/walletLoader.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/forward/walletLoader.ts b/src/forward/walletLoader.ts index b4115c4..986a632 100644 --- a/src/forward/walletLoader.ts +++ b/src/forward/walletLoader.ts @@ -14,7 +14,6 @@ export function loadWallet(encodedArgs: string | undefined): Wallet { throw Error("Failed to parse appDeveloperSecret JSON"); } - //TODO: Remove infura keys at some point const infuraProjectId = appDeveloperSecretJson.infuraProjectId; if (infuraProjectId == undefined) { throw Error("Failed to parse `infuraProjectId` from decoded secret JSON"); @@ -30,10 +29,8 @@ export function loadWallet(encodedArgs: string | undefined): Wallet { throw Error("Failed to parse `targetPrivateKey` from decoded secret JSON"); } - // const provider = new ethers.providers.InfuraProvider(chain, { - // projectId: infuraProjectId, - // projectSecret: infuraProjectSecret, - // }); + //TODO: Remove infura keys at some point + //const provider = getProvider(chainId, infuraProjectId, infuraProjectSecret); const wallet = new ethers.Wallet(targetPrivateKey); console.log( @@ -49,3 +46,14 @@ interface OracleArgs { infuraProjectSecret?: string; targetPrivateKey?: string; } + +// function getProvider( +// chainId: number, +// infuraProjectId: string, +// infuraProjectSecret: string +// ) { +// return new ethers.providers.InfuraProvider(chainId, { +// projectId: infuraProjectId, +// projectSecret: infuraProjectSecret, +// }); +// } From e47398cd679a1472014609181cdc98ac608bf90b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Wed, 2 Nov 2022 17:28:55 +0100 Subject: [PATCH 21/35] Remove completed TODO (unit tests run on CI) --- docker/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index beff950..a8f922d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,7 +2,6 @@ FROM node:14-alpine3.10 AS builder COPY package*.json tsconfig.json ./ COPY src/ /src/ RUN npm ci -# Future note: Run unit tests automatically RUN npm run build # Copy generated *.js to /app so we can use them RUN cp -R app/src/* ./app From 182ae80388612268b40f55e2a06f44f102363762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Mon, 7 Nov 2022 14:30:01 +0100 Subject: [PATCH 22/35] Validate chain ids input, handle sub request failures and use ethers randomBytes --- src/dapp.ts | 10 ++++------ src/forward/forwardHandler.ts | 9 +++++++-- src/forward/forwardSigner.ts | 4 +--- src/validators.js | 11 ++++++++++- tests/dapp.test.ts | 2 +- tests/forward/forwardSigner.test.ts | 10 ++++++---- 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/src/dapp.ts b/src/dapp.ts index 3da2315..f0ef4c0 100644 --- a/src/dapp.ts +++ b/src/dapp.ts @@ -1,7 +1,7 @@ import fsPromises from "fs/promises"; import utils from "./utils"; import { apiCall } from "./caller"; -import { jsonParamSetSchema } from "./validators"; +import { jsonParamSetSchema, targetChainsSchema } from "./validators"; import { getInputFilePath, extractDataset, @@ -75,11 +75,9 @@ const start = async () => { } // Native command line arguments - 0:node, 1:app.ts, 2:arg1 - const requestedChainIds = - process.argv.length > 2 - ? // Parse chainIds, sort them, remove duplicates, cast them from string to number - Array.from(new Set(process.argv[2].split(",").sort()), Number) - : undefined; + const requestedChainIds = await targetChainsSchema().validate( + process.argv[2] + ); if (requestedChainIds) { console.log( "User requesting updates on foreign blockchains [chains:%s]", diff --git a/src/forward/forwardHandler.ts b/src/forward/forwardHandler.ts index 9231cf1..8df9b4c 100644 --- a/src/forward/forwardHandler.ts +++ b/src/forward/forwardHandler.ts @@ -53,9 +53,14 @@ export async function triggerForwardRequests( oracleId, encodedValue, onChainConfig - ); + // catch promise rejection and convert it to false to prevent Promise.all rejection + ).catch((e) => false); - return await postForwardRequest(signedForwardRequest, oracleId, taskId); + return await postForwardRequest( + signedForwardRequest, + oracleId, + taskId + ).catch((e) => false); }) ); diff --git a/src/forward/forwardSigner.ts b/src/forward/forwardSigner.ts index 5a9bead..4837b64 100644 --- a/src/forward/forwardSigner.ts +++ b/src/forward/forwardSigner.ts @@ -50,9 +50,7 @@ export async function signForwardRequest( gas: ( await receiveResult.getGasEstimate(taskId, encodedValue, reporterAddress) ).toString(), - salt: ethers.utils.keccak256( - ethers.utils.toUtf8Bytes(Math.random().toString()) - ), + salt: ethers.utils.hexlify(ethers.utils.randomBytes(32)), data: receiveResult.getData(taskId, encodedValue), }; diff --git a/src/validators.js b/src/validators.js index 7846a64..b476ad6 100644 --- a/src/validators.js +++ b/src/validators.js @@ -1,4 +1,4 @@ -const { string, object, array } = require("yup"); +const { string, number, object, array } = require("yup"); const { getAddress } = require("ethers").utils; const jp = require("jsonpath"); const { API_KEY_PLACEHOLDER } = require("./conf"); @@ -306,9 +306,18 @@ const jsonParamSetSchema = () => } ); +const chainIdSchema = () => number().integer(); + +// Parse chainIds, sort them, remove duplicates, cast them to number +const targetChainsSchema = () => + array() + .transform((value, originalValue) => Array.from(new Set(originalValue.split(',').sort()))) + .of(chainIdSchema().required()); + module.exports = { rawParamsSchema, strictCallParamsSchema, strictParamSetSchema, jsonParamSetSchema, + targetChainsSchema }; diff --git a/tests/dapp.test.ts b/tests/dapp.test.ts index dcd9855..faaf449 100644 --- a/tests/dapp.test.ts +++ b/tests/dapp.test.ts @@ -28,7 +28,7 @@ describe("dapp", () => { jest.mocked(triggerForwardRequests).mockReturnValue(Promise.resolve(true)); - process.argv = ["", "", "5,80001"]; // Goerli & Mumbai Polygon + process.argv = ["", "", "80001,5"]; // Goerli & Mumbai Polygon process.env.IEXEC_TASK_ID = "0x0000000000000000000000000000000000000000000000000000000000000abc"; process.env.IEXEC_APP_DEVELOPER_SECRET = buildAppSecret( diff --git a/tests/forward/forwardSigner.test.ts b/tests/forward/forwardSigner.test.ts index c05a458..7b40c70 100644 --- a/tests/forward/forwardSigner.test.ts +++ b/tests/forward/forwardSigner.test.ts @@ -1,5 +1,5 @@ import { ClassicOracle } from "@iexec/generic-oracle-contracts/typechain"; -import { Wallet, BigNumber } from "ethers"; +import { Wallet, BigNumber, utils } from "ethers"; import { signForwardRequest } from "../../src/forward/forwardSigner"; import { ReceiveResultContractFunction } from "../../src/forward/oracleContractWrapper"; @@ -30,7 +30,9 @@ describe("Forward signer", () => { .mocked(ReceiveResultContractFunction) .mockImplementation(() => receiveResultMock); - jest.spyOn(Math, "random").mockReturnValue(123456789); + jest.spyOn(utils, "randomBytes").mockReturnValue( + utils.toUtf8Bytes("01234567890123456789012345678901") //size 32 + ); const signedRequest = await signForwardRequest( CHAIN_ID, @@ -67,11 +69,11 @@ describe("Forward signer", () => { to: "0x0000000000000000000000000000000000000002", value: "0", gas: "100000", - salt: "0x2a359feeb8e488a1af2c03b908b3ed7990400555db73e1421181d97cac004d48", + salt: "0x3031323334353637383930313233343536373839303132333435363738393031", data: "0xabcd", }, }, - sign: "0x85457e9405fbe9a75a8604f4bd678ce8a5276215ea78cad77376b406f7a325f67efb764efd0d2d4a01b35a1eb2e47e90625a1f7056e5c3853068bca64e1b51e21c", + sign: "0xfa561b74cd3fc10cdee22beed8ede242d8da31c9e203e2ee1c34fe015cadc50603d93681ad71cd682ac4f8049633a78ceaafd92c69595d9c60402f54ad442fe91b", }); }); }); From 7fc1f6b134bd0544e636b6de639dc877d29e6329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Tue, 8 Nov 2022 09:35:06 +0100 Subject: [PATCH 23/35] Sort chain IDs as numbers --- src/validators.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/validators.js b/src/validators.js index b476ad6..2c18da5 100644 --- a/src/validators.js +++ b/src/validators.js @@ -311,7 +311,8 @@ const chainIdSchema = () => number().integer(); // Parse chainIds, sort them, remove duplicates, cast them to number const targetChainsSchema = () => array() - .transform((value, originalValue) => Array.from(new Set(originalValue.split(',').sort()))) + .transform((value, originalValue) => + Array.from(new Set(originalValue.split(','))).sort((a, b) => a - b)) .of(chainIdSchema().required()); module.exports = { From 2543daea52c32ac99c8ddf9bb7e380f0365dc2d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Tue, 8 Nov 2022 16:48:46 +0100 Subject: [PATCH 24/35] ReceiveResultContractFunction with IOracleConsumer --- src/forward/forwardEnvironment.ts | 8 ++++---- src/forward/oracleContractWrapper.ts | 9 +++++---- tests/forward/forwardSigner.test.ts | 4 ++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/forward/forwardEnvironment.ts b/src/forward/forwardEnvironment.ts index 4499cb3..f2fe0db 100644 --- a/src/forward/forwardEnvironment.ts +++ b/src/forward/forwardEnvironment.ts @@ -6,14 +6,14 @@ export const forwarderApiUrl = "https://forwarder.dev-oracle-factory.iex.ec"; const chainIdToOnChainConfig: Record = { // Goerli 5: { - forwarder: "0xc83de370A0D1C99F3D3D9e77bd930520ded81fFA", - oracle: "0x8Ad317241854b1A29A06cE5478e6B92FA09Cd03a", + forwarder: "0x2aD6aD4F35cf7354fE703da74F459690dBcC12bf", + oracle: "0x8dFf608952ADCDa4cF7320324Db1ef44001BE79b", providerUrl: undefined, //use default provider from ethers }, // Mumbai Polygon 80001: { - forwarder: "0x6843aA5A3a777Ae750DD9d93a9D0fdF99e061b53", - oracle: "0x68bDfa911178f72CEA7BCFd0FeEbbA4cDDE24eCF", + forwarder: "0xa715674ecf9D14141421190b6f8Acf20686b54d7", + oracle: "0x330031CF7e6E2C318Dba230fe25A7f39fD3644EA", providerUrl: "https://rpc-mumbai.maticvigil.com", }, }; diff --git a/src/forward/oracleContractWrapper.ts b/src/forward/oracleContractWrapper.ts index 4b04c7b..8847af4 100644 --- a/src/forward/oracleContractWrapper.ts +++ b/src/forward/oracleContractWrapper.ts @@ -1,13 +1,14 @@ import { ethers } from "ethers"; -import { ClassicOracle__factory } from "@iexec/generic-oracle-contracts/typechain"; +import { IOracleConsumer__factory } from "@iexec/generic-oracle-contracts/typechain"; export class ReceiveResultContractFunction { oracleContract; //public for tests constructor(oracleAddress: string, provider: ethers.providers.BaseProvider) { - this.oracleContract = new ClassicOracle__factory() - .attach(oracleAddress) - .connect(provider); + this.oracleContract = IOracleConsumer__factory.connect( + oracleAddress, + provider + ); } getGasEstimate( diff --git a/tests/forward/forwardSigner.test.ts b/tests/forward/forwardSigner.test.ts index 7b40c70..7b232bf 100644 --- a/tests/forward/forwardSigner.test.ts +++ b/tests/forward/forwardSigner.test.ts @@ -1,4 +1,4 @@ -import { ClassicOracle } from "@iexec/generic-oracle-contracts/typechain"; +import { IOracleConsumer } from "@iexec/generic-oracle-contracts/typechain"; import { Wallet, BigNumber, utils } from "ethers"; import { signForwardRequest } from "../../src/forward/forwardSigner"; import { ReceiveResultContractFunction } from "../../src/forward/oracleContractWrapper"; @@ -21,7 +21,7 @@ describe("Forward signer", () => { test("should", async () => { const receiveResultMock = { - oracleContract: {} as ClassicOracle, + oracleContract: {} as IOracleConsumer, getGasEstimate: () => Promise.resolve(BigNumber.from("100000")), getData: () => "0xabcd", } as ReceiveResultContractFunction; From ac2766aa50e44d7385fa5a3e17b6e53afb1a3893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Mon, 14 Nov 2022 11:31:58 +0100 Subject: [PATCH 25/35] Init config file for local/dev/prod --- .gitignore | 1 + Jenkinsfile | 1 + Readme.md | 2 +- config.dev.json | 15 +++++++++++ config.local.json | 15 +++++++++++ config.prod.json | 15 +++++++++++ docker/.env-build-dev | 1 + docker/.env-build-local | 1 + docker/.env-build-prod | 1 + docker/Dockerfile | 7 +++++ src/forward/forwardEnvironment.ts | 34 +++++++++++------------- src/forward/forwardSender.ts | 4 +-- src/forward/forwardSigner.ts | 6 ++--- tests/forward/forwardEnvironment.test.ts | 12 +++++++++ tests/forward/forwardSender.test.ts | 8 ++++-- 15 files changed, 96 insertions(+), 27 deletions(-) create mode 100644 config.dev.json create mode 100644 config.local.json create mode 100644 config.prod.json create mode 100644 docker/.env-build-dev create mode 100644 docker/.env-build-local create mode 100644 docker/.env-build-prod create mode 100644 tests/forward/forwardEnvironment.test.ts diff --git a/.gitignore b/.gitignore index 296b1a7..ed023de 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ node_modules/ coverage/ app/ tests/test_out/computed.json +src/config.json diff --git a/Jenkinsfile b/Jenkinsfile index 1a46604..8cb283b 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -5,6 +5,7 @@ docker.image('node:16-alpine').inside { checkout scm sh ''' npm ci + cp config.prod.json src/config.json npm run ci-test ''' archiveArtifacts artifacts: 'coverage/' diff --git a/Readme.md b/Readme.md index 592a591..90a6b18 100644 --- a/Readme.md +++ b/Readme.md @@ -58,7 +58,7 @@ TARGET_PRIVATE_KEY= ## Build native image ``` -docker image build -f docker/Dockerfile -t generic-oracle-dapp:local . +docker image build -f docker/Dockerfile -t generic-oracle-dapp:local --build-arg CONFIG_FILE=config.prod.json . ``` ## Build TEE debug image diff --git a/config.dev.json b/config.dev.json new file mode 100644 index 0000000..176d3cd --- /dev/null +++ b/config.dev.json @@ -0,0 +1,15 @@ +{ + "forwarderApiUrl": "https://forwarder.dev-oracle-factory.iex.ec", + "onChainConfig": { + "5": { + "forwarder": "0x2aD6aD4F35cf7354fE703da74F459690dBcC12bf", + "oracle": "0x8dFf608952ADCDa4cF7320324Db1ef44001BE79b", + "providerUrl": "" + }, + "80001": { + "forwarder": "0xa715674ecf9D14141421190b6f8Acf20686b54d7", + "oracle": "0x330031CF7e6E2C318Dba230fe25A7f39fD3644EA", + "providerUrl": "https://rpc-mumbai.maticvigil.com" + } + } +} \ No newline at end of file diff --git a/config.local.json b/config.local.json new file mode 100644 index 0000000..d98332e --- /dev/null +++ b/config.local.json @@ -0,0 +1,15 @@ +{ + "forwarderApiUrl": "http://localhost:5000", + "onChainConfig": { + "5": { + "forwarder": "0x2aD6aD4F35cf7354fE703da74F459690dBcC12bf", + "oracle": "0x8dFf608952ADCDa4cF7320324Db1ef44001BE79b", + "providerUrl": "" + }, + "80001": { + "forwarder": "0xa715674ecf9D14141421190b6f8Acf20686b54d7", + "oracle": "0x330031CF7e6E2C318Dba230fe25A7f39fD3644EA", + "providerUrl": "https://rpc-mumbai.maticvigil.com" + } + } +} \ No newline at end of file diff --git a/config.prod.json b/config.prod.json new file mode 100644 index 0000000..eaf2d23 --- /dev/null +++ b/config.prod.json @@ -0,0 +1,15 @@ +{ + "forwarderApiUrl": "https://forwarder.oracle-factory.iex.ec", + "onChainConfig": { + "5": { + "forwarder": "0x", + "oracle": "0x", + "providerUrl": "" + }, + "80001": { + "forwarder": "0x", + "oracle": "0x", + "providerUrl": "https://rpc-mumbai.maticvigil.com" + } + } +} \ No newline at end of file diff --git a/docker/.env-build-dev b/docker/.env-build-dev new file mode 100644 index 0000000..4923752 --- /dev/null +++ b/docker/.env-build-dev @@ -0,0 +1 @@ +ARG CONFIG_FILE=config.prod.json \ No newline at end of file diff --git a/docker/.env-build-local b/docker/.env-build-local new file mode 100644 index 0000000..1d7566f --- /dev/null +++ b/docker/.env-build-local @@ -0,0 +1 @@ +ARG CONFIG_FILE=config.local.json \ No newline at end of file diff --git a/docker/.env-build-prod b/docker/.env-build-prod new file mode 100644 index 0000000..4923752 --- /dev/null +++ b/docker/.env-build-prod @@ -0,0 +1 @@ +ARG CONFIG_FILE=config.prod.json \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile index a8f922d..dcfd402 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,6 +1,13 @@ FROM node:14-alpine3.10 AS builder COPY package*.json tsconfig.json ./ COPY src/ /src/ +# region-config-file +ARG CONFIG_FILE +RUN echo "CONFIG_FILE : ${CONFIG_FILE}" +RUN test -n "$CONFIG_FILE" +COPY $CONFIG_FILE src/config.json +RUN cat src/config.json +# endregion-config-file RUN npm ci RUN npm run build # Copy generated *.js to /app so we can use them diff --git a/src/forward/forwardEnvironment.ts b/src/forward/forwardEnvironment.ts index f2fe0db..95c697a 100644 --- a/src/forward/forwardEnvironment.ts +++ b/src/forward/forwardEnvironment.ts @@ -1,24 +1,20 @@ -// localhost -//export const forwarderApiUrl = "http://localhost:5000"; -// dev.iex.ec -export const forwarderApiUrl = "https://forwarder.dev-oracle-factory.iex.ec"; +import config from "../config.json"; + +function getConfig(): Config { + return config; +} + +export function getForwarderApiUrl() { + return getConfig().forwarderApiUrl; +} -const chainIdToOnChainConfig: Record = { - // Goerli - 5: { - forwarder: "0x2aD6aD4F35cf7354fE703da74F459690dBcC12bf", - oracle: "0x8dFf608952ADCDa4cF7320324Db1ef44001BE79b", - providerUrl: undefined, //use default provider from ethers - }, - // Mumbai Polygon - 80001: { - forwarder: "0xa715674ecf9D14141421190b6f8Acf20686b54d7", - oracle: "0x330031CF7e6E2C318Dba230fe25A7f39fD3644EA", - providerUrl: "https://rpc-mumbai.maticvigil.com", - }, -}; export function getOnChainConfig(chainId: number) { - return chainIdToOnChainConfig[chainId]; + return getConfig().onChainConfig[chainId]; +} + +export interface Config { + forwarderApiUrl: string; + onChainConfig: Record; } export interface OnChainConfig { diff --git a/src/forward/forwardSender.ts b/src/forward/forwardSender.ts index a3e0493..4e8e8a0 100644 --- a/src/forward/forwardSender.ts +++ b/src/forward/forwardSender.ts @@ -1,12 +1,12 @@ import fetch from "node-fetch"; -import { forwarderApiUrl } from "./forwardEnvironment"; +import { getForwarderApiUrl } from "./forwardEnvironment"; export async function postForwardRequest( signedForwardRequest: any, oracleId: string, taskId: string ): Promise { - const response = await fetch(forwarderApiUrl + "/forward", { + const response = await fetch(getForwarderApiUrl() + "/forward", { method: "post", body: JSON.stringify(signedForwardRequest), headers: { "Content-Type": "application/json" }, diff --git a/src/forward/forwardSigner.ts b/src/forward/forwardSigner.ts index 4837b64..8728fc1 100644 --- a/src/forward/forwardSigner.ts +++ b/src/forward/forwardSigner.ts @@ -32,10 +32,10 @@ export async function signForwardRequest( const providerUrl = onChainConfig.providerUrl; let provider; - if (providerUrl) { - provider = new ethers.providers.JsonRpcProvider(providerUrl); - } else { + if (providerUrl == undefined || providerUrl == "") { provider = ethers.getDefaultProvider(chainId); + } else { + provider = new ethers.providers.JsonRpcProvider(providerUrl); } const receiveResult = new ReceiveResultContractFunction( diff --git a/tests/forward/forwardEnvironment.test.ts b/tests/forward/forwardEnvironment.test.ts new file mode 100644 index 0000000..8af7fb3 --- /dev/null +++ b/tests/forward/forwardEnvironment.test.ts @@ -0,0 +1,12 @@ +import { + getForwarderApiUrl, + getOnChainConfig, +} from "../../src/forward/forwardEnvironment"; + +describe("Environment", () => { + test("should get environment", async () => { + expect(getForwarderApiUrl()).toContain("http"); + expect(getOnChainConfig(5)).not.toBeUndefined(); + expect(getOnChainConfig(80001)).not.toBeUndefined(); + }); +}); diff --git a/tests/forward/forwardSender.test.ts b/tests/forward/forwardSender.test.ts index 562a678..6a09159 100644 --- a/tests/forward/forwardSender.test.ts +++ b/tests/forward/forwardSender.test.ts @@ -1,8 +1,9 @@ import fetch from "node-fetch"; import { postForwardRequest } from "../../src/forward/forwardSender"; -import { forwarderApiUrl } from "../../src/forward/forwardEnvironment"; +import { getForwarderApiUrl } from "../../src/forward/forwardEnvironment"; jest.mock("node-fetch"); +jest.mock("../../src/forward/forwardEnvironment"); const TASK_ID = "0x0000000000000000000000000000000000000000000000000000000000000abc"; @@ -17,10 +18,13 @@ describe("Forward signer", () => { (fetch as any).mockImplementation(async () => ({ ok: true, })); + jest + .mocked(getForwarderApiUrl) + .mockImplementation(() => "http://forwarder"); await postForwardRequest({ some: "request" }, ORACLE_ID, TASK_ID); - expect(fetch).toHaveBeenCalledWith(forwarderApiUrl + "/forward", { + expect(fetch).toHaveBeenCalledWith("http://forwarder/forward", { method: "post", body: '{"some":"request"}', headers: { "Content-Type": "application/json" }, From d100d4379778e9ad8f4c9b7211c8b0830393db9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Mon, 14 Nov 2022 11:38:36 +0100 Subject: [PATCH 26/35] Add end of file on config files --- config.dev.json | 2 +- config.local.json | 2 +- config.prod.json | 2 +- docker/.env-build-dev | 2 +- docker/.env-build-local | 2 +- docker/.env-build-prod | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/config.dev.json b/config.dev.json index 176d3cd..c7a2d8d 100644 --- a/config.dev.json +++ b/config.dev.json @@ -12,4 +12,4 @@ "providerUrl": "https://rpc-mumbai.maticvigil.com" } } -} \ No newline at end of file +} diff --git a/config.local.json b/config.local.json index d98332e..64c59d1 100644 --- a/config.local.json +++ b/config.local.json @@ -12,4 +12,4 @@ "providerUrl": "https://rpc-mumbai.maticvigil.com" } } -} \ No newline at end of file +} diff --git a/config.prod.json b/config.prod.json index eaf2d23..84138db 100644 --- a/config.prod.json +++ b/config.prod.json @@ -12,4 +12,4 @@ "providerUrl": "https://rpc-mumbai.maticvigil.com" } } -} \ No newline at end of file +} diff --git a/docker/.env-build-dev b/docker/.env-build-dev index 4923752..a5e4eff 100644 --- a/docker/.env-build-dev +++ b/docker/.env-build-dev @@ -1 +1 @@ -ARG CONFIG_FILE=config.prod.json \ No newline at end of file +ARG CONFIG_FILE=config.dev.json diff --git a/docker/.env-build-local b/docker/.env-build-local index 1d7566f..e0d08a7 100644 --- a/docker/.env-build-local +++ b/docker/.env-build-local @@ -1 +1 @@ -ARG CONFIG_FILE=config.local.json \ No newline at end of file +ARG CONFIG_FILE=config.local.json diff --git a/docker/.env-build-prod b/docker/.env-build-prod index 4923752..70ec990 100644 --- a/docker/.env-build-prod +++ b/docker/.env-build-prod @@ -1 +1 @@ -ARG CONFIG_FILE=config.prod.json \ No newline at end of file +ARG CONFIG_FILE=config.prod.json From 2a09aeb934bc8e3ef4fedf238455d23a9b6ca15a 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: Mon, 14 Nov 2022 15:02:58 +0100 Subject: [PATCH 27/35] Update tests/forward/forwardEnvironment.test.ts Co-authored-by: pjt <26487010+PierreJeanjacquot@users.noreply.github.com> --- tests/forward/forwardEnvironment.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/forward/forwardEnvironment.test.ts b/tests/forward/forwardEnvironment.test.ts index 8af7fb3..1ee98ee 100644 --- a/tests/forward/forwardEnvironment.test.ts +++ b/tests/forward/forwardEnvironment.test.ts @@ -6,7 +6,7 @@ import { describe("Environment", () => { test("should get environment", async () => { expect(getForwarderApiUrl()).toContain("http"); - expect(getOnChainConfig(5)).not.toBeUndefined(); - expect(getOnChainConfig(80001)).not.toBeUndefined(); + expect(getOnChainConfig(5)).toBeDefined(); + expect(getOnChainConfig(80001)).toBeDefined(); }); }); From ec899783ee5296629d68698c4637530972f7bb55 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: Mon, 14 Nov 2022 15:03:14 +0100 Subject: [PATCH 28/35] Update Readme.md Co-authored-by: pjt <26487010+PierreJeanjacquot@users.noreply.github.com> --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 90a6b18..a2b882a 100644 --- a/Readme.md +++ b/Readme.md @@ -58,7 +58,7 @@ TARGET_PRIVATE_KEY= ## Build native image ``` -docker image build -f docker/Dockerfile -t generic-oracle-dapp:local --build-arg CONFIG_FILE=config.prod.json . +docker image build -f docker/Dockerfile -t generic-oracle-dapp:local --build-arg CONFIG_FILE=config.local.json . ``` ## Build TEE debug image From a9928d741a154311037d81f94ea149417e3a186d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Mon, 14 Nov 2022 15:20:24 +0100 Subject: [PATCH 29/35] Simplify get ethers provider --- src/forward/forwardSigner.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/forward/forwardSigner.ts b/src/forward/forwardSigner.ts index 8728fc1..8dbfc8c 100644 --- a/src/forward/forwardSigner.ts +++ b/src/forward/forwardSigner.ts @@ -31,12 +31,7 @@ export async function signForwardRequest( const oracleAddress = onChainConfig.oracle; const providerUrl = onChainConfig.providerUrl; - let provider; - if (providerUrl == undefined || providerUrl == "") { - provider = ethers.getDefaultProvider(chainId); - } else { - provider = new ethers.providers.JsonRpcProvider(providerUrl); - } + const provider = ethers.getDefaultProvider(providerUrl || chainId); const receiveResult = new ReceiveResultContractFunction( oracleAddress, From 992ffbb6ff1c3c3399c5426e26d970deffad1957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Tue, 15 Nov 2022 17:51:00 +0100 Subject: [PATCH 30/35] Add prod config --- config.prod.json | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/config.prod.json b/config.prod.json index 84138db..3786b8e 100644 --- a/config.prod.json +++ b/config.prod.json @@ -1,14 +1,24 @@ { "forwarderApiUrl": "https://forwarder.oracle-factory.iex.ec", "onChainConfig": { + "1": { + "forwarder": "0xc684E8645c8414812f22918146d72d1071E722AE", + "oracle": "0x36dA71ccAd7A67053f0a4d9D5f55b725C9A25A3E", + "providerUrl": "" + }, "5": { - "forwarder": "0x", - "oracle": "0x", + "forwarder": "0xc684E8645c8414812f22918146d72d1071E722AE", + "oracle": "0x36dA71ccAd7A67053f0a4d9D5f55b725C9A25A3E", "providerUrl": "" }, + "137": { + "forwarder": "0xc684E8645c8414812f22918146d72d1071E722AE", + "oracle": "0x36dA71ccAd7A67053f0a4d9D5f55b725C9A25A3E", + "providerUrl": "https://rpc-mainnet.maticvigil.com" + }, "80001": { - "forwarder": "0x", - "oracle": "0x", + "forwarder": "0xc684E8645c8414812f22918146d72d1071E722AE", + "oracle": "0x36dA71ccAd7A67053f0a4d9D5f55b725C9A25A3E", "providerUrl": "https://rpc-mumbai.maticvigil.com" } } From 0ba8c3d6fbca9813c2d191d2d7673749447d0397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Thu, 17 Nov 2022 17:25:28 +0100 Subject: [PATCH 31/35] Add buildAppSecret script --- scripts/buildAppSecret.ts | 9 +++++++++ tsconfig.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 scripts/buildAppSecret.ts diff --git a/scripts/buildAppSecret.ts b/scripts/buildAppSecret.ts new file mode 100644 index 0000000..f3865bc --- /dev/null +++ b/scripts/buildAppSecret.ts @@ -0,0 +1,9 @@ +import { buildAppSecret } from "../tests/utils"; + +process.env.INFURA_PROJECT_ID = ""; +process.env.INFURA_PROJECT_SECRET = ""; +const encodedAppSecret = buildAppSecret(process.argv[2]); +console.log(encodedAppSecret); + +// Enable it to verify final app secret +//console.log("App secret recovered [decodedAppSecret:%s]", Buffer.from(encodedAppSecret, "base64").toString()); diff --git a/tsconfig.json b/tsconfig.json index ba9fd12..4661ac0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,5 +21,5 @@ "src", "tests", "node_modules/@iexec/generic-oracle-contracts/typechain" - ] +, "scripts/buildAppSecret.ts" ] } From 1eca69ff09e63606d8ae4738c1985a4ffad14fea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Thu, 17 Nov 2022 17:27:52 +0100 Subject: [PATCH 32/35] Add runTask script and update Readme --- Readme.md | 10 +++++++++- iexec.json | 14 +++++++------- runTask.sh | 13 +++++++++++++ 3 files changed, 29 insertions(+), 8 deletions(-) create mode 100755 runTask.sh diff --git a/Readme.md b/Readme.md index a2b882a..df9ad42 100644 --- a/Readme.md +++ b/Readme.md @@ -81,13 +81,16 @@ npm run scone ``` iexec app deploy --chain bellecour ``` +``` +npx ts-node scripts/buildAppSecret.ts +``` ``` iexec-core-cli app push-owner-secret --secret=$MY_SECRETS --sms= --wallet-path=/tmp/wallet.json --wallet-password ``` or ``` -iexec app push-secret --secret-value $MY_SECRETS --chain bellecour +iexec app push-secret --chain bellecour --secret-value $MY_SECRETS ``` ``` @@ -96,6 +99,11 @@ iexec order sign --app --chain bellecour ### As requester: trigger crosschain app +Update iexec.json, make requester wallet file available and run: +``` +runTask.sh <0xrequesterAddress> +``` +or ``` iexec order sign --request --chain bellecour ``` diff --git a/iexec.json b/iexec.json index 1e344e9..44d5b32 100644 --- a/iexec.json +++ b/iexec.json @@ -16,10 +16,10 @@ "callback": "0x0000000000000000000000000000000000000000" }, "app": { - "owner": "0x15Bd06807eF0F2284a9C5baeAA2EF4d5a88eB72A", - "name": "generic-oracle-dapp-xc", + "owner": "<0xAppDeveloper>", + "name": "generic-oracle-dapp", "type": "DOCKER", - "multiaddr": "docker.io/iexechub/generic-oracle-dapp:feature-d0b90550-sconify-5.3.15-production", + "multiaddr": "docker.io/iexechub/generic-oracle-dapp:", "checksum": "0xf7485b254816519e4e49aaaa66e31a99877f3fd19feb2dfe880513b44bfe4fde", "mrenclave": { "provider": "SCONE", @@ -31,7 +31,7 @@ }, "order": { "requestorder": { - "app": "0xdce234BF640235549D4D95C4764B1863FfDa7cF4", + "app": "<0xApp>", "appmaxprice": "0", "dataset": "0x0000000000000000000000000000000000000000", "datasetmaxprice": "0", @@ -46,15 +46,15 @@ "params": { "iexec_args": "5,80001", "iexec_input_files": [ - "https://raw.githubusercontent.com/iExecBlockchainComputing/generic-oracle-dapp/feature/metatxs-to-forwarder-api/tests/test_files/input_file_no_dataset.json" + "https://raw.githubusercontent.com/iExecBlockchainComputing/generic-oracle-dapp/develop/tests/test_files/input_file_no_dataset.json" ], "iexec_result_encryption": false, "iexec_developer_logger": true }, - "requester": "0x15Bd06807eF0F2284a9C5baeAA2EF4d5a88eB72A" + "requester": "<0xRequester>" }, "apporder": { - "app": "0xdce234BF640235549D4D95C4764B1863FfDa7cF4", + "app": "<0xApp>", "appprice": "0", "volume": "1000", "tag": "0x0000000000000000000000000000000000000000000000000000000000000001", diff --git a/runTask.sh b/runTask.sh new file mode 100755 index 0000000..b617232 --- /dev/null +++ b/runTask.sh @@ -0,0 +1,13 @@ +#!/bin/bash +# Usage: ./runTask.sh <0xrequesterAddress> + +REQUESTER=$1 +# Bellecour prod pool +WORKERPOOL=0xEb14Dc854A8873e419183c81a657d025EC70276b +WALLET="--wallet-address $REQUESTER" +CHAIN="--chain bellecour" + +iexec order sign --request $CHAIN +WORKERPOOL_ORDER=$(iexec orderbook workerpool --tag tee $WORKERPOOL $CHAIN --raw | jq -r .workerpoolOrders[0].orderHash) +echo "WORKERPOOL_ORDER: $WORKERPOOL_ORDER" +iexec order fill --workerpool $WORKERPOOL_ORDER $CHAIN $WALLET \ No newline at end of file From 97018b15e7e2b07f38c5378568db011249d9b911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Thu, 17 Nov 2022 17:32:24 +0100 Subject: [PATCH 33/35] Add some iexec-sdk generated files to gitignore --- .gitignore | 4 ++++ deployed.json | 5 ----- 2 files changed, 4 insertions(+), 5 deletions(-) delete mode 100644 deployed.json diff --git a/.gitignore b/.gitignore index ed023de..e506373 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ coverage/ app/ tests/test_out/computed.json src/config.json + +chain.json +deployed.json +orders.json diff --git a/deployed.json b/deployed.json deleted file mode 100644 index c3c10d4..0000000 --- a/deployed.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "app": { - "134": "0xdce234BF640235549D4D95C4764B1863FfDa7cF4" - } -} From 107ce7fab4e281fec30e83b803406db72d174110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Thu, 17 Nov 2022 17:34:59 +0100 Subject: [PATCH 34/35] Upgrade dependency to @iexec/generic-oracle-contracts@2.2.0 --- package-lock.json | 14 +++++++------- package.json | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 55b2106..9f32ef0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.1.0", "license": "Apache-2.0", "dependencies": { - "@iexec/generic-oracle-contracts": "^2.1.0", + "@iexec/generic-oracle-contracts": "^2.2.0", "big.js": "^6.0.3", "ethers": "^5.6.8", "jsonpath": "^1.1.0", @@ -1341,9 +1341,9 @@ "dev": true }, "node_modules/@iexec/generic-oracle-contracts": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@iexec/generic-oracle-contracts/-/generic-oracle-contracts-2.1.0.tgz", - "integrity": "sha512-SXvJ4OHhu9GJUVqstOCqBISVxZ9KNNKpbMNrrJX1O53LxvxoDBgLAv0bEij+GjY1ANNDzKAbcUZZ0GDwA2JttQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@iexec/generic-oracle-contracts/-/generic-oracle-contracts-2.2.0.tgz", + "integrity": "sha512-1uGIabzRmN9X0Y3VIpEFOC4/sfiz2SCJsY5qLiO0nGi6ZkuS5B/JGhDCRuOMTjXojO/vTimeWaxVbNgxU96G+A==", "dependencies": { "openzeppelin-contracts-solc-0.8": "npm:@openzeppelin/contracts@^4.7.0" }, @@ -8809,9 +8809,9 @@ "dev": true }, "@iexec/generic-oracle-contracts": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@iexec/generic-oracle-contracts/-/generic-oracle-contracts-2.1.0.tgz", - "integrity": "sha512-SXvJ4OHhu9GJUVqstOCqBISVxZ9KNNKpbMNrrJX1O53LxvxoDBgLAv0bEij+GjY1ANNDzKAbcUZZ0GDwA2JttQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@iexec/generic-oracle-contracts/-/generic-oracle-contracts-2.2.0.tgz", + "integrity": "sha512-1uGIabzRmN9X0Y3VIpEFOC4/sfiz2SCJsY5qLiO0nGi6ZkuS5B/JGhDCRuOMTjXojO/vTimeWaxVbNgxU96G+A==", "requires": { "fsevents": "^2.3.2", "openzeppelin-contracts-solc-0.8": "npm:@openzeppelin/contracts@^4.7.0" diff --git a/package.json b/package.json index 9c72795..0b2b229 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "This application is meant to build a docker container usable in SGX iexec tasks. The dapp take an input file containing a param set in a JSON format. The param set describe the request that should be done to the target API in order to get the wanted data.", "main": "src/app.js", "dependencies": { - "@iexec/generic-oracle-contracts": "^2.1.0", + "@iexec/generic-oracle-contracts": "^2.2.0", "big.js": "^6.0.3", "ethers": "^5.6.8", "jsonpath": "^1.1.0", From 20cebe98234bfd3154db2dcfce7e2ba9f0353839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9my=20James=20Toussaint?= Date: Thu, 17 Nov 2022 17:43:35 +0100 Subject: [PATCH 35/35] Add missing line at end of file --- runTask.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runTask.sh b/runTask.sh index b617232..59df1bc 100755 --- a/runTask.sh +++ b/runTask.sh @@ -10,4 +10,4 @@ CHAIN="--chain bellecour" iexec order sign --request $CHAIN WORKERPOOL_ORDER=$(iexec orderbook workerpool --tag tee $WORKERPOOL $CHAIN --raw | jq -r .workerpoolOrders[0].orderHash) echo "WORKERPOOL_ORDER: $WORKERPOOL_ORDER" -iexec order fill --workerpool $WORKERPOOL_ORDER $CHAIN $WALLET \ No newline at end of file +iexec order fill --workerpool $WORKERPOOL_ORDER $CHAIN $WALLET