From c2be56fa44246de7c046ddce57a799a5cb356c6b Mon Sep 17 00:00:00 2001 From: Krzysztof Spisak-Spisacki <50660061+kss-espeo@users.noreply.github.com> Date: Wed, 14 Aug 2019 14:37:03 +0200 Subject: [PATCH] Feature/closed api access (#55) * turn on no implicit any flag * Make implicite any explicite * freeze packages * fix unit test * changed any to specific types * replaced any with specific types * removed redundant files, made chai config consistent across test cases, TS-ed some JS remnants * naming private/protected methods with appropriate keywords rather than '_' prefix * some more any's replaced with specific types * added specific types to random provider code * some more type changes * removed unnecessary type * cosmetics * made RequestExecutorStrategy (and everything above) oblivious of RequestExecutor-specific setup * implemented decrypt url use case + rename env variable + add some missing tests * missing test + cleanup * added case for invalid encryption * added test for long data * stylistic changes as per CR * documentation for encrypted + missing documentation for random * documentation for example request encryption * documentation for example request encryption * added doc info about required Python version --- .env.bitbucket | 2 +- .env.local | 2 +- .env.test | 2 +- .env.tpl | 2 +- README.md | 2 +- docs/configuration.rst | 2 +- docs/getting-started.rst | 7 +- docs/making-requests.rst | 78 ++++- docs/prerequisites.rst | 3 + package-lock.json | 276 ++++++++++++++++++ package.json | 1 + .../common/usecase/DecryptUrlUseCase.spec.ts | 88 ++++++ .../common/usecase/DecryptUrlUseCase.ts | 37 +++ src/domain/common/utils/error/ErrorCode.ts | 1 + .../utils/error/InvalidContentTypeError.ts | 2 +- .../utils/error/InvalidEncryptionError.ts | 10 + .../RequestExecutorStrategy.spec.ts | 18 +- .../RequestExecutorStrategy.ts | 5 +- .../requestExecutor/UrlRequestExecutor.ts | 15 +- src/index.ts | 22 +- .../ethereum/createAndUnlockWeb3.ts | 2 +- 21 files changed, 536 insertions(+), 41 deletions(-) create mode 100644 src/domain/common/usecase/DecryptUrlUseCase.spec.ts create mode 100644 src/domain/common/usecase/DecryptUrlUseCase.ts create mode 100644 src/domain/common/utils/error/InvalidEncryptionError.ts diff --git a/.env.bitbucket b/.env.bitbucket index da76fb5..5f504af 100644 --- a/.env.bitbucket +++ b/.env.bitbucket @@ -1,4 +1,4 @@ -PUBLIC_KEY=0x1df62f291b2e969fb0849d99d9ce41e2f137006e +ADDRESS=0x1df62f291b2e969fb0849d99d9ce41e2f137006e PRIVATE_KEY=0xb0057716d5917badaf911b193b12b910811c1497b5bada8d7711f758981c3773 ORACLE_ADDRESS=0xcfeb869f69431e42cdb54a4f4f105c19c080a601 DATABASE_URL=localhost:27017 diff --git a/.env.local b/.env.local index a8828b3..3186fd6 100644 --- a/.env.local +++ b/.env.local @@ -1,4 +1,4 @@ -PUBLIC_KEY=0x1df62f291b2e969fb0849d99d9ce41e2f137006e +ADDRESS=0x1df62f291b2e969fb0849d99d9ce41e2f137006e PRIVATE_KEY=0xb0057716d5917badaf911b193b12b910811c1497b5bada8d7711f758981c3773 ORACLE_ADDRESS=0xcfeb869f69431e42cdb54a4f4f105c19c080a601 DATABASE_URL=mongo:27017 diff --git a/.env.test b/.env.test index e346d50..d722d8c 100644 --- a/.env.test +++ b/.env.test @@ -1,4 +1,4 @@ -PUBLIC_KEY=0x1df62f291b2e969fb0849d99d9ce41e2f137006e +ADDRESS=0x1df62f291b2e969fb0849d99d9ce41e2f137006e PRIVATE_KEY=0xb0057716d5917badaf911b193b12b910811c1497b5bada8d7711f758981c3773 ORACLE_ADDRESS=0xcfeb869f69431e42cdb54a4f4f105c19c080a601 DATABASE_URL=localhost:37017 diff --git a/.env.tpl b/.env.tpl index 2c9c3e6..a694174 100644 --- a/.env.tpl +++ b/.env.tpl @@ -1,4 +1,4 @@ -PUBLIC_KEY=0x1df62f291b2e969fb0849d99d9ce41e2f137006e +ADDRESS=0x1df62f291b2e969fb0849d99d9ce41e2f137006e PRIVATE_KEY=0xb0057716d5917badaf911b193b12b910811c1497b5bada8d7711f758981c3773 ORACLE_ADDRESS=0xcfeb869f69431e42cdb54a4f4f105c19c080a601 DATABASE_URL=mongo:27017 diff --git a/README.md b/README.md index f7f2af0..2c1b5c5 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Oracle is a concept of getting information from outside of the blockchain to the ## Installation As a first step please clone `.env.tpl` file into `.env` and fill with values: -- `PUBLIC_KEY` - address of the server's account, from which it sends the results of request to the smart contracts +- `ADDRESS` - address of the server's account, from which it sends the results of request to the smart contracts - `PRIVATE_KEY` - private key of the server's account - `ORACLE_ADDRESS` - address of the `Oracle` smart contract - `DATABASE_URL` - URL with port for MongoDB connection diff --git a/docs/configuration.rst b/docs/configuration.rst index 97cea52..4c2263c 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -8,7 +8,7 @@ This section is going to explain parameters from them. Default parameters are se Server ====== -- `PUBLIC_KEY` - address of the server's account, from which it sends the results of request to the smart contracts +- `ADDRESS` - address of the server's account, from which it sends the results of request to the smart contracts - `PRIVATE_KEY` - private key of the server's account - `ORACLE_ADDRESS` - address of the `Oracle` smart contract - `DATABASE_URL` - URL with port for MongoDB connection diff --git a/docs/getting-started.rst b/docs/getting-started.rst index b970b3f..bd9ce8e 100644 --- a/docs/getting-started.rst +++ b/docs/getting-started.rst @@ -123,7 +123,8 @@ If you did everything correctly you should see something similar to logsBloom: '0x00040000000000000010000000000000000000000000000000000000000000000000000000000002000000000000010000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000004000000000000000000200000000000000000000000000000000000000000000000000000020400000005000000000000000000000000000000000000000000000000000000000000000000000', v: '0x1b', r: '0x21052fa282f9723d221ef288cec6e947cb2ba0ef3f1d470f5dc8845806f66977', - s: '0x1723cb7b4288f6ae32f3495d666522859150fe3d0c8e4debd3a80d452f940f50' }, + s: '0x1723cb7b4288f6ae32f3495d666522859150fe3d0c8e4debd3a80d452f940f50' + }, logs: [ { logIndex: 1, transactionIndex: 0, @@ -133,7 +134,9 @@ If you did everything correctly you should see something similar to address: '0x9561c133dd8580860b6b7e504bc5aa500f0f06a7', type: 'mined', event: 'DataRequestedFromOracle', - args: [Object] } ] } + args: [Object] + } ] + } Server logs ----------- diff --git a/docs/making-requests.rst b/docs/making-requests.rst index 35be9bb..8530073 100644 --- a/docs/making-requests.rst +++ b/docs/making-requests.rst @@ -7,12 +7,12 @@ Making requests Request types ============= -At your disposal you have two types of requests. You can use either an instant request, which should fulfill as fast as possible or schedule a delayed request, performed at a specific point of time in the future. +You have two types of requests at your disposal. You can use either an instant request, which should fulfill as fast as possible or schedule a delayed request, performed at a specific point of time in the future. Instant requests ~~~~~~~~~~~~~~~~ -To perform instant request you need to call :code:`request(string _url)` method in :code:`Oracle` contract passing as it's only parameter url wrapped in a format type you want to get back. More about format types in `Response formats`_ section. +To perform instant request you need to call :code:`request(string _url)` method in :code:`Oracle` contract passing as its only parameter an url wrapped in a format type you want to get back. More about format types in `Response formats`_ section. Delayed requests ~~~~~~~~~~~~~~~~ @@ -24,17 +24,21 @@ The delay parameter can be given in two ways: as a unix timestamp or as a relati Request sources =============== -Currently we support only open REST API sources. In the future we plan to implement: +Currently we support following request sources: -- IPFS +- open REST api - random data + +In the future we plan to implement: + +- IPFS - closed APIs Response formats ================ -Currently we support JSON, XML and HTML formats. In the future we plan to support also raw binary data. +For REST api responses, we support JSON, XML and HTML formats. In the future we plan to support also raw binary data. JSON format ~~~~~~~~~~~ @@ -109,6 +113,69 @@ Example response value: "W3Schools Online Web Tutorials" error: 0 +ENCRYPTED url fragments +~~~~~~~~~~~ + +For all URL datasources (XML, HTML, JSON) it is also possible to encrypt entirety, part or parts of your query using encrypted() tag. You might want to do that if you wouldn't like some parts of your URL to be visible to everyone on blockchain. An obvious example would be using some API key as a parameter to your REST query. +In order to pass part of your query secretly, simply encrypt it using Gardener public key and wrap it in encrypted() tag. Gardener will decrypt it using its private key and then process it as usual. +Any assymetric encryption implementation may be used as long as it produces a stringified version of a following object: {iv, ephemPublicKey, ciphertext, mac} . We recommend using https://www.npmjs.com/package/eth-crypto as shown below. + +Example request encryption +:: + +import EthCrypto from 'eth-crypto'; + +const gardenerPublicKey = '9c691b945b14656b98edbf4d3657290c65cad377bca44da4d54e88cd2bbdefb2e063267b06183029fea5017567653c0fb6c4e3426843381ad7e09014b2d384cf' // if you want to create it programmatically, derive it from Gardener address or Gardener private key if you are owner of the instance +const cipher = await EthCrypto.encryptWithPublicKey(gardenerPublicKey, 'SECRET_DATA'); +constEncryptedSecret = EthCrypto.cipher.stringify(cipher); + +Example request +:: + + json(https://api.coindesk.com/v1/bpi/historical/close.json?currency=encrypted(c317e44653b8cc3e3ca7f6d9686711c60269a5fd41490868ad8b9cc054836af9d074670241860036e3534fddd6dd73995f14c211da51478025ffb45d9f53b8abb7e681700d13c0d58c0a441fdfd5c6dc57810b451c607338c0851cdc8066421968)).disclaimer + json(https://api.coindesk.com/v1/bpi/historical/close.json?currency=encrypted(c317e44653b8cc3e3ca7f6d9686711c60269a5fd41490868ad8b9cc054836af9d074670241860036e3534fddd6dd73995f14c211da51478025ffb45d9f53b8abb7e681700d13c0d58c0a441fdfd5c6dc57810b451c607338c0851cdc8066421968)&someOtherParam=encrypted(someOtherEncryptedValue)).disclaimer + + +Example response +:: + + value: "This data was produced from the CoinDesk Bitcoin Price Index. BPI value data returned as EUR." + error: 0 + +RANDOM datasource +~~~~~~~~~~~ + +Random numbers can be generated using either random.org service or a dedicated SGX application. This is configurable by setting SGX_ENABLED in your .env file to either true or false. +There are many benefits of generating random numbers using SGX. We haven't fully leveraged this exciting technology, but after we do, every number will be securely and verifiably generated with the ONLY Third Trusted Party being Intel. That's right, you don't even have to trust whoever hosts a Gardener instance! This is further explained in our article: https://medium.com/gardeneroracle/random-number-generation-in-gardener-1660e5c25e00 . You are also read up about Intel SGX technical details, this is a good starting point: https://software.intel.com/en-us/sgx +Using SGX requires a specific hardware and OS (is your environment compatible? check it here https://github.com/ayeks/SGX-hardware) as well as SGX PSW installed. If you don't feel like doing that, you are welcome to use a random.org source which can be considered a less secure but easy to use fallback option. + +.. note:: + + In order to switch between SGX and random.org way of generating random numbers, use SGX_ENABLED in your .env file. + +To generate a random value use the random() wrapper with inclusive upper and lower bounds specified. +Both of these bounds should be integers. For random.org acceptable bounds are [-1000000000,1000000000] while for SGX they are defined by 8-byte long datatype: [-9223372036854775808, 9223372036854775807] + +Example requests +:: + + random(10,20) + random(-2,33) + random(-124354325432,-34325253) + + +Example response +:: + + value: 13 + error: 0 + + value: -2 + error: 0 + + value: -9532532335 + error: 0 + Response error codes ==================== @@ -119,6 +186,7 @@ Error name Error code Description ========================== ========== =========== INVALID_URL 1000 Text between type(...) wrapper isn't valid url INVALID_CONTENT_TYPE 1001 This response format wrapper isn't supported +INVALID_ENCRYPTION 1002 Invalid encrypted data. This probably means your data was not encrypted using Gardener public key. INVALID_SELECTOR 4000 The selector isn't valid JsonPath or XmlPath NO_MATCHING_ELEMENTS_FOUND 4004 No elements found for given selector INTERNAL_SERVER_ERROR 5000 Unhandled error happens inside Gardener Server diff --git a/docs/prerequisites.rst b/docs/prerequisites.rst index 5a606c1..c55a73a 100644 --- a/docs/prerequisites.rst +++ b/docs/prerequisites.rst @@ -27,3 +27,6 @@ Make sure you have successfully installed it by typing in command line .. note:: Keep in mind that your Node.js version has to be at least 7.6 as gardener uses async/await pattern. +.. note:: + Please make sure to have Python 2.7.16 (not newer!) installed. Reason for that is node-ffi library that we use is not compatible with newest Python. + diff --git a/package-lock.json b/package-lock.json index 002d2d1..df441c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -409,6 +409,11 @@ "negotiator": "0.6.2" } }, + "acorn": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.0.tgz", + "integrity": "sha512-vvZ8PwswGTM15ZXyk3I+SvpTm8UbF8iRnGiU/f9TPU6By7K1XTvfvusFtoQt0WYycudFSYW2lrJDivhBlGovvQ==" + }, "aes-js": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", @@ -552,6 +557,22 @@ "is-buffer": "^2.0.2" } }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + } + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -575,6 +596,14 @@ "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz", "integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE=" }, + "bip66": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bip66/-/bip66-1.1.5.tgz", + "integrity": "sha1-AfqHSHhcpwlV1QESF9GzE5lpyiI=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "bl": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", @@ -999,6 +1028,11 @@ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" }, + "core-js": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" + }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -1367,6 +1401,16 @@ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-6.0.0.tgz", "integrity": "sha512-FlWbnhgjtwD+uNLUGHbMykMOYQaTivdHEmYwAKFjn6GKe/CqY0fNae93ZHTd20snh9ZLr8mTzIL9m0APQ1pjQg==" }, + "drbg.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz", + "integrity": "sha1-Pja2xCs3BDgjzbwzLVjzHiRFSAs=", + "requires": { + "browserify-aes": "^1.0.6", + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4" + } + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -1381,6 +1425,50 @@ "safer-buffer": "^2.1.0" } }, + "eccrypto": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/eccrypto/-/eccrypto-1.1.1.tgz", + "integrity": "sha512-uTLiIWyckVdddMywc6ArT4n3qKJDAUgUXV7A5yzUpqxz2lG++5+hPODEhtOf3C/qBLuCIUHjSt2ttSdteKzeMw==", + "requires": { + "acorn": "6.0.0", + "elliptic": "6.0.2", + "es6-promise": "3.0.2", + "nan": "2.11.1", + "secp256k1": "2.0.4" + }, + "dependencies": { + "elliptic": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.0.2.tgz", + "integrity": "sha1-IZuWzZKqmIXZHTHB/ULqpetEg6k=", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "inherits": "^2.0.1" + } + }, + "nan": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", + "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==" + }, + "secp256k1": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-2.0.4.tgz", + "integrity": "sha1-BFPkuhzABIWRIXTvShxZsPG2+kk=", + "optional": true, + "requires": { + "bindings": "^1.2.1", + "bluebird": "^3.0.2", + "bn.js": "^4.1.1", + "elliptic": "^6.0.1", + "nan": "^2.0.9", + "object-assign": "^4.0.1" + } + } + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -1478,6 +1566,11 @@ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", "dev": true }, + "es6-promise": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz", + "integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y=" + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1528,6 +1621,92 @@ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" }, + "eth-crypto": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/eth-crypto/-/eth-crypto-1.4.0.tgz", + "integrity": "sha512-BWeQFoGvutBnA4rH7IQTUHHbV+YludDueVxlqNXG3XXOMdrulLSqAPkxv8h4AlfBJnn50zJBmoyhJ11UIbz8EA==", + "requires": { + "@types/bn.js": "4.11.5", + "babel-runtime": "6.26.0", + "eccrypto": "1.1.1", + "eth-lib": "0.2.8", + "ethereumjs-tx": "2.1.0", + "ethereumjs-util": "6.1.0", + "ethers": "4.0.33", + "secp256k1": "3.7.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", + "integrity": "sha512-ArJ7x1WcWOlSpzdoTBX8vkwlkSQ85CjjifSZtV4co64vWxSV8geWfPI9x4SVYu3DSxnX4yWFVTtGL+j9DUFLNw==", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "ethers": { + "version": "4.0.33", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.33.tgz", + "integrity": "sha512-lAHkSPzBe0Vj+JrhmkEHLtUEKEheVktIjGDyE9gbzF4zf1vibjYgB57LraDHu4/ItqWVkztgsm8GWqcDMN+6vQ==", + "requires": { + "@types/node": "^10.3.2", + "aes-js": "3.0.0", + "bn.js": "^4.4.0", + "elliptic": "6.3.3", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.4", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + }, + "dependencies": { + "elliptic": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz", + "integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "inherits": "^2.0.1" + } + } + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + }, + "scrypt-js": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.4.tgz", + "integrity": "sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==" + }, + "setimmediate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", + "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=" + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" + } + } + }, "eth-ens-namehash": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", @@ -1558,6 +1737,34 @@ "xhr-request-promise": "^0.1.2" } }, + "ethereumjs-common": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ethereumjs-common/-/ethereumjs-common-1.3.0.tgz", + "integrity": "sha512-/jdFHyHOIS3FiAnunwRZ+oNulFtNNSHyWii3PaNHReOUmBAxij7KMyZLKh0tE16JEsJtXOVz1ceYuq++ILzv+g==" + }, + "ethereumjs-tx": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-2.1.0.tgz", + "integrity": "sha512-q1PFhR5i93OjcoE0G3GGz7XvnpLiddcUSKr28hmMUzVHvvc/+PHmQTx4NrGQUUny7qBq9tEIcvMivdB7uphKtA==", + "requires": { + "ethereumjs-common": "^1.3.0", + "ethereumjs-util": "^6.0.0" + } + }, + "ethereumjs-util": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.1.0.tgz", + "integrity": "sha512-URESKMFbDeJxnAxPppnk2fN6Y3BIatn9fwn76Lm8bQlt+s52TpG8dN9M66MLPuRAiAOIqL3dfwqWJf0sd0fL0Q==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "ethjs-util": "0.1.6", + "keccak": "^1.0.2", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1", + "secp256k1": "^3.0.1" + } + }, "ethers": { "version": "4.0.0-beta.1", "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.0-beta.1.tgz", @@ -1628,6 +1835,15 @@ } } }, + "ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "requires": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + } + }, "eventemitter3": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-1.1.1.tgz", @@ -1806,6 +2022,11 @@ "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=" }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, "finalhandler": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz", @@ -2617,6 +2838,17 @@ "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.0.tgz", "integrity": "sha512-6hHxsp9e6zQU8nXsP+02HGWXwTkOEw6IROhF2ZA28cYbUk4eJ6QbtZvdqZOdD9YPKghG3apk5eOCvs+tLl3lRg==" }, + "keccak": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-1.4.0.tgz", + "integrity": "sha512-eZVaCpblK5formjPjeTBik7TAg+pqnDrMHIffSvi9Lh7PQgM1+hSzakUeZFCk9DVVG0dacZJuaz2ntwlzZUIBw==", + "requires": { + "bindings": "^1.2.1", + "inherits": "^2.0.3", + "nan": "^2.2.1", + "safe-buffer": "^5.1.0" + } + }, "keccakjs": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/keccakjs/-/keccakjs-0.2.3.tgz", @@ -3985,6 +4217,15 @@ "inherits": "^2.0.1" } }, + "rlp": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.3.tgz", + "integrity": "sha512-l6YVrI7+d2vpW6D6rS05x2Xrmq8oW7v3pieZOJKBEdjuTF4Kz/iwk55Zyh1Zaz+KOB2kC8+2jZlp2u9L4tTzCQ==", + "requires": { + "bn.js": "^4.11.1", + "safe-buffer": "^5.1.1" + } + }, "rxjs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.2.tgz", @@ -4042,6 +4283,41 @@ "pbkdf2": "^3.0.3" } }, + "secp256k1": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.7.1.tgz", + "integrity": "sha512-1cf8sbnRreXrQFdH6qsg2H71Xw91fCCS9Yp021GnUNJzWJS/py96fS4lHbnTnouLp08Xj6jBoBB6V78Tdbdu5g==", + "requires": { + "bindings": "^1.5.0", + "bip66": "^1.1.5", + "bn.js": "^4.11.8", + "create-hash": "^1.2.0", + "drbg.js": "^1.0.1", + "elliptic": "^6.4.1", + "nan": "^2.14.0", + "safe-buffer": "^5.1.2" + }, + "dependencies": { + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==" + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + } + } + }, "seek-bzip": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.5.tgz", diff --git a/package.json b/package.json index 215ab4c..8ce309b 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "axios": "0.19.0", "delay": "4.1.0", "dotenv": "6.0.0", + "eth-crypto": "1.4.0", "execution-time": "1.3.0", "express": "4.16.3", "ffi": "2.3.0", diff --git a/src/domain/common/usecase/DecryptUrlUseCase.spec.ts b/src/domain/common/usecase/DecryptUrlUseCase.spec.ts new file mode 100644 index 0000000..950cd33 --- /dev/null +++ b/src/domain/common/usecase/DecryptUrlUseCase.spec.ts @@ -0,0 +1,88 @@ +import {expect} from '@core/config/configuredChai'; +import EthCrypto from 'eth-crypto'; +import {beforeEach, describe, it} from 'mocha'; +import InvalidEncryptionError from '../utils/error/InvalidEncryptionError'; + +import DecryptUrlUseCase from './DecryptUrlUseCase'; + +describe('DecryptUrlUseCase', () => { + const someSecret = 'EUR'; + const someOtherSecret = 'TOP_SECRET'; + const somePrivateKey = '0x0000000000000000000000000000000000000000000000000000000000000001'; + const publicKey = EthCrypto.publicKeyByPrivateKey(somePrivateKey); + let sut: DecryptUrlUseCase; + + beforeEach(() => { + sut = new DecryptUrlUseCase(somePrivateKey); + }); + + it('should decrypt url with secret string', async () => { + // given + const cipher = await EthCrypto.encryptWithPublicKey(publicKey, someSecret); + + const encryptedText = `json(https://some.url?apiKey=encrypted(${(EthCrypto.cipher.stringify(cipher))})).chartName`; + const expected = `json(https://some.url?apiKey=${someSecret}).chartName`; + // when + const decrypted = await sut.decrypt(encryptedText); + // then + expect(decrypted).to.be.equal(expected); + }); + + it('should decrypt url with multiple secret strings', async () => { + // given + const cipher = await EthCrypto.encryptWithPublicKey(publicKey, someSecret); + const anotherCipher = await EthCrypto.encryptWithPublicKey(publicKey, someOtherSecret); + + const encryptedText = `json(https://some.url?apiKey=encrypted(${(EthCrypto.cipher.stringify(cipher))})\ +&nonSecret=rumourHasIt&secret=encrypted(${(EthCrypto.cipher.stringify(anotherCipher))})).chartName`; + const expected = `json(https://some.url?apiKey=${someSecret}&nonSecret=rumourHasIt&secret=${someOtherSecret}).chartName`; + // when + const decrypted = await sut.decrypt(encryptedText); + // then + expect(decrypted).to.be.equal(expected); + }); + + it('should decrypt url with secret containing non-standard characters', async () => { + // given + const nonStandardSecret = '$^#$^!)$#@^($#^#$)()'; + const cipher = await EthCrypto.encryptWithPublicKey(publicKey, nonStandardSecret); + const encryptedText = `json(https://some.url?secret=encrypted(${(EthCrypto.cipher.stringify(cipher))})`; + const expected = `json(https://some.url?secret=${nonStandardSecret}`; + // when + const decrypted = await sut.decrypt(encryptedText); + // then + expect(decrypted).to.be.equal(expected); + }); + + it('should change nothing if url has no encrypted parts', async () => { + // given + const nonEncryptedUrl = `json(https://some.url?noSecret=papa()dsgs)`; + // when + const decrypted = await sut.decrypt(nonEncryptedUrl); + // then + expect(decrypted).to.be.equal(nonEncryptedUrl); + }); + + it('should throw InvalidRequestError for invalid encrypted data', async () => { + // given + const invalidPublicKey = EthCrypto.publicKeyByPrivateKey('0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'); + const cipher = await EthCrypto.encryptWithPublicKey(invalidPublicKey, someSecret); + const encryptedText = `json(https://some.url?apiKey=encrypted(${(EthCrypto.cipher.stringify(cipher))})).chartName`; + // when + // then + return expect(sut.decrypt(encryptedText)).to.be.rejectedWith(InvalidEncryptionError, + 'Invalid encryption data. Expected a stringified object {iv, ephemPublicKey, ciphertext, mac} encrypted with gardener public key'); + }); + + it('should decrypt secret much longer than public key', async () => { + // given + const bigSecret = 'a'.repeat(publicKey.length * 10); + const cipher = await EthCrypto.encryptWithPublicKey(publicKey, bigSecret); + const encryptedText = `json(https://some.url?apiKey=encrypted(${(EthCrypto.cipher.stringify(cipher))})).chartName`; + const expected = `json(https://some.url?apiKey=${bigSecret}).chartName`; + // when + const decrypted = await sut.decrypt(encryptedText); + // then + expect(decrypted).to.be.equal(expected); + }); +}); diff --git a/src/domain/common/usecase/DecryptUrlUseCase.ts b/src/domain/common/usecase/DecryptUrlUseCase.ts new file mode 100644 index 0000000..35398e4 --- /dev/null +++ b/src/domain/common/usecase/DecryptUrlUseCase.ts @@ -0,0 +1,37 @@ +import EthCrypto from 'eth-crypto'; + +import InvalidEncryptionError from '@core/domain/common/utils/error/InvalidEncryptionError'; + +class DecryptUrlUseCase { + private typeRegex = new RegExp(/(encrypted\(.*?\))/g); + + constructor(private readonly privateKey: string) { + } + + async decrypt(url: string): Promise { + if (!this.typeRegex.test(url)) { + return url; + } + + for (const group of url.match(this.typeRegex)) { + const encrypted = group.substr(group.indexOf('(') + 1, group.length - 'encrypted()'.length); + + let decrypted: string; + try { + decrypted = await EthCrypto.decryptWithPrivateKey( + this.privateKey, + EthCrypto.cipher.parse(encrypted), + ); + } catch (e) { + throw new InvalidEncryptionError('Invalid encryption data. Expected a stringified object ' + + '{iv, ephemPublicKey, ciphertext, mac} encrypted with gardener public key') + ; + } + url = url.replace(group, decrypted); + } + + return url; + } +} + +export default DecryptUrlUseCase; diff --git a/src/domain/common/utils/error/ErrorCode.ts b/src/domain/common/utils/error/ErrorCode.ts index 22f136c..a2c655d 100644 --- a/src/domain/common/utils/error/ErrorCode.ts +++ b/src/domain/common/utils/error/ErrorCode.ts @@ -1,6 +1,7 @@ export enum ErrorCode { INVALID_URL = 1000, INVALID_CONTENT_TYPE = 1001, + INVALID_ENCRYPTION = 1002, INVALID_SELECTOR_DATA = 4000, NO_MATCHING_ELEMENTS_FOUND = 4004, INTERNAL_SERVER_ERROR = 5000, diff --git a/src/domain/common/utils/error/InvalidContentTypeError.ts b/src/domain/common/utils/error/InvalidContentTypeError.ts index 2273493..a9aa708 100644 --- a/src/domain/common/utils/error/InvalidContentTypeError.ts +++ b/src/domain/common/utils/error/InvalidContentTypeError.ts @@ -2,7 +2,7 @@ import { ErrorCode } from './ErrorCode'; import InvalidRequestError from './InvalidRequestError'; class InvalidContentTypeError extends InvalidRequestError { - constructor(message: string ) { + constructor(message: string) { super(message, ErrorCode.INVALID_CONTENT_TYPE); } } diff --git a/src/domain/common/utils/error/InvalidEncryptionError.ts b/src/domain/common/utils/error/InvalidEncryptionError.ts new file mode 100644 index 0000000..870572d --- /dev/null +++ b/src/domain/common/utils/error/InvalidEncryptionError.ts @@ -0,0 +1,10 @@ +import { ErrorCode } from './ErrorCode'; +import InvalidRequestError from './InvalidRequestError'; + +class InvalidEncryptionError extends InvalidRequestError { + constructor(message: string) { + super(message, ErrorCode.INVALID_ENCRYPTION); + } +} + +export default InvalidEncryptionError; diff --git a/src/domain/request/requestExecutor/RequestExecutorStrategy.spec.ts b/src/domain/request/requestExecutor/RequestExecutorStrategy.spec.ts index fb1159a..f0a1a68 100644 --- a/src/domain/request/requestExecutor/RequestExecutorStrategy.spec.ts +++ b/src/domain/request/requestExecutor/RequestExecutorStrategy.spec.ts @@ -3,6 +3,7 @@ import { assert } from '@core/config/configuredChai'; import { beforeEach, describe, it } from 'mocha'; import InvalidContentTypeError from '@core/domain/common/utils/error/InvalidContentTypeError'; +import RandomRequestExecutor from './RandomRequestExecutor'; import RequestExecutorStrategy from './RequestExecutorStrategy'; import UrlRequestExecutor from './UrlRequestExecutor'; @@ -13,7 +14,7 @@ describe('RequestExecutorStrategy', () => { sut = new RequestExecutorStrategy(null, null, null, null); }); - it('should create UrlProvider for json', () => { + it('should create UrlRequestExecutor for json', () => { // given const contentType = 'ipfs'; // when @@ -22,7 +23,7 @@ describe('RequestExecutorStrategy', () => { assert.instanceOf(result, UrlRequestExecutor, 'Incorrect request executor type'); }); - it('should create UrlProvider for xml', () => { + it('should create UrlRequestExecutor for xml', () => { // given const contentType = 'xml'; // when @@ -31,7 +32,7 @@ describe('RequestExecutorStrategy', () => { assert.instanceOf(result, UrlRequestExecutor, 'Incorrect request executor type'); }); - it('should create UrlProvider for html', () => { + it('should create UrlRequestExecutor for html', () => { // given const contentType = 'html'; // when @@ -40,7 +41,16 @@ describe('RequestExecutorStrategy', () => { assert.instanceOf(result, UrlRequestExecutor, 'Incorrect request executor type'); }); - it('should create UrlProvider for ipfs', () => { + it('should create RandomRequestExecutor for random', () => { + // given + const contentType = 'random'; + // when + const result = sut.create(contentType); + // then + assert.instanceOf(result, RandomRequestExecutor, 'Incorrect request executor type'); + }); + + it('should create UrlRequestExecutor for ipfs', () => { // given const contentType = 'ipfs'; // when diff --git a/src/domain/request/requestExecutor/RequestExecutorStrategy.ts b/src/domain/request/requestExecutor/RequestExecutorStrategy.ts index 427e6a4..972ccfd 100644 --- a/src/domain/request/requestExecutor/RequestExecutorStrategy.ts +++ b/src/domain/request/requestExecutor/RequestExecutorStrategy.ts @@ -1,6 +1,5 @@ import { LoggerPort } from '@core/domain/common/port'; import RequestExecutor from '@core/domain/common/port/RequestExecutorPort'; -import SelectDataUseCase from '@core/domain/common/usecase/SelectDataUseCase'; import InvalidContentTypeError from '@core/domain/common/utils/error/InvalidContentTypeError'; import RandomRequestExecutor from '@core/domain/request/requestExecutor/RandomRequestExecutor'; import UrlRequestExecutor from './UrlRequestExecutor'; @@ -11,11 +10,11 @@ class RequestExecutorStrategy { constructor( sgxEnabled: boolean, randomDotOrgApiKey: string, - selectDataUseCase: SelectDataUseCase, + privateKey: string, logger: LoggerPort, ) { this.requestExecutors = [ - new UrlRequestExecutor(selectDataUseCase, logger), + new UrlRequestExecutor(privateKey, logger), new RandomRequestExecutor(sgxEnabled, randomDotOrgApiKey, logger), ]; } diff --git a/src/domain/request/requestExecutor/UrlRequestExecutor.ts b/src/domain/request/requestExecutor/UrlRequestExecutor.ts index 1f83b43..a0df3ec 100644 --- a/src/domain/request/requestExecutor/UrlRequestExecutor.ts +++ b/src/domain/request/requestExecutor/UrlRequestExecutor.ts @@ -1,6 +1,11 @@ import UrlDataFetcher from '@core/application/dataFetcher/AxiosUrlDataFetcherAdapter'; +import IdentitySelector from '@core/application/selector/identity/IdentitySelectorAdapter'; +import JsonSelector from '@core/application/selector/json/JsonSelectorAdapter'; +import XmlSelector from '@core/application/selector/xml/XmlSelectorAdapter'; +import DataSelectorFinder from '@core/domain/common/DataSelectorFinder'; import {LoggerPort} from '@core/domain/common/port'; import RequestExecutor from '@core/domain/common/port/RequestExecutorPort'; +import DecryptUrlUseCase from '@core/domain/common/usecase/DecryptUrlUseCase'; import FetchDataUseCase from '@core/domain/common/usecase/FetchDataUseCase'; import SelectDataUseCase from '@core/domain/common/usecase/SelectDataUseCase'; import Request from '@core/domain/request/Request'; @@ -8,11 +13,18 @@ import Response from '@core/domain/response/Response'; class UrlRequestExecutor implements RequestExecutor { private fetchDataUseCase: FetchDataUseCase; + private selectDataUseCase: SelectDataUseCase; + private decryptUrlUseCase: DecryptUrlUseCase; constructor( - private readonly selectDataUseCase: SelectDataUseCase, + privateKey: string, private readonly logger: LoggerPort, ) { this.fetchDataUseCase = new FetchDataUseCase(new UrlDataFetcher(), this.logger); + this.selectDataUseCase = new SelectDataUseCase( + new DataSelectorFinder([new JsonSelector(), new XmlSelector(), new IdentitySelector()]), + logger, + ); + this.decryptUrlUseCase = new DecryptUrlUseCase(privateKey); } canHandle(contentType: string): boolean { @@ -21,6 +33,7 @@ class UrlRequestExecutor implements RequestExecutor { async execute(request: Request): Promise { const response = new Response(request.id); + request.url = await this.decryptUrlUseCase.decrypt(request.url); const fetchedData = await this.fetchDataUseCase.fetchData(request); response.addFetchedData(fetchedData); diff --git a/src/index.ts b/src/index.ts index c9299ca..f2a5682 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,12 +17,6 @@ import oracleAbi from './config/abi/oracle.abi'; import { CreateRequestEventHandler, CurrentBlockEventHandler } from './infrastructure/event'; import { ExecuteReadyRequestsScheduler, MarkValidRequestsAsReadyScheduler } from './infrastructure/scheduling'; -import { - IdentitySelectorAdapter as IdentitySelector, - JsonSelectorAdapter as JsonSelector, - XmlSelectorAdapter as XmlSelector, -} from './application/selector'; - import { CreateRequestUseCase, ExecuteReadyRequestsUseCase, @@ -31,7 +25,6 @@ import { import { CheckHealthStatusUseCase, - SelectDataUseCase, } from './domain/common/usecase'; import { @@ -39,8 +32,6 @@ import { SendResponseToOracleUseCase, } from './domain/blockchain/usecase'; -import DataSelectorFinder from './domain/common/DataSelectorFinder'; - import BlockListener from './infrastructure/blockchain/BlockListener'; import { AbiItem } from 'web3-utils/types'; @@ -49,7 +40,7 @@ import PersistenceConnectionInitializer from './infrastructure/persistence/Persi const { DATABASE_URL, DATABASE_NAME, PERSISTENCE, START_BLOCK, SAFE_BLOCK_DELAY, API_PORT, - SGX_ENABLED, RANDOMDOTORG_API_KEY, + SGX_ENABLED, RANDOMDOTORG_API_KEY, PRIVATE_KEY, } = process.env; const persistenceOptions = { @@ -63,10 +54,6 @@ const logger = new Logger(); const requestRepository = RequestRepositoryFactory.create(PERSISTENCE, logger); const responseRepository = ResponseRepositoryFactory.create(PERSISTENCE, logger); const oracle = new Oracle(web3, oracleAbi as AbiItem[], process.env.ORACLE_ADDRESS); -const jsonSelector = new JsonSelector(); -const xmlSelector = new XmlSelector(); -const identitySelector = new IdentitySelector(); -const dataSelectorFinder = new DataSelectorFinder([jsonSelector, xmlSelector, identitySelector]); const createRequestUseCase = new CreateRequestUseCase(requestRepository, logger); const fetchNewOracleRequestsUseCase = new FetchNewOracleRequestsUseCase( @@ -78,16 +65,15 @@ const markValidRequestsAsReadyUseCase = new MarkValidRequestsAsReadyUseCase( requestRepository, logger, ); -const selectDataUseCase = new SelectDataUseCase(dataSelectorFinder, logger); -const requestExecutorFactory = new RequestExecutorStrategy( - SGX_ENABLED.toLowerCase() === 'true', RANDOMDOTORG_API_KEY, selectDataUseCase, logger, +const requestExecutorStrategy = new RequestExecutorStrategy( + SGX_ENABLED.toLowerCase() === 'true', RANDOMDOTORG_API_KEY, PRIVATE_KEY, logger, ); const sendResponseToOracleUseCase = new SendResponseToOracleUseCase(oracle, logger); const executeReadyRequestsUseCase = new ExecuteReadyRequestsUseCase( sendResponseToOracleUseCase, requestRepository, responseRepository, - requestExecutorFactory, + requestExecutorStrategy, logger, ); const checkHealthStatusUseCase = new CheckHealthStatusUseCase(); diff --git a/src/infrastructure/blockchain/ethereum/createAndUnlockWeb3.ts b/src/infrastructure/blockchain/ethereum/createAndUnlockWeb3.ts index 2493f8e..551b2ff 100644 --- a/src/infrastructure/blockchain/ethereum/createAndUnlockWeb3.ts +++ b/src/infrastructure/blockchain/ethereum/createAndUnlockWeb3.ts @@ -3,6 +3,6 @@ import Web3 from 'web3'; const web3 = new Web3(process.env.NODE_URL); web3.eth.accounts.wallet.add(process.env.PRIVATE_KEY); -web3.eth.defaultAccount = process.env.PUBLIC_KEY; +web3.eth.defaultAccount = process.env.ADDRESS; export default web3;