From c037471e609a32c2545182e00ab94cf4793308b3 Mon Sep 17 00:00:00 2001 From: pedro Date: Thu, 6 May 2021 17:44:50 -0300 Subject: [PATCH 1/4] Add Etherscan API to have a fallback option Send the signed transaction throw etherscan as well as to the ethereum node Use Etherscan gas price oracle to avoid gas price too low for transactions or too high --- federator/README.md | 7 +- federator/config/config.sample.js | 3 +- federator/package-lock.json | 345 +++++++++++++++++-------- federator/package.json | 1 + federator/src/lib/Federator.js | 9 +- federator/src/lib/TransactionSender.js | 113 +++++++- federator/src/main.js | 7 +- 7 files changed, 350 insertions(+), 135 deletions(-) diff --git a/federator/README.md b/federator/README.md index 328380442..969096a25 100644 --- a/federator/README.md +++ b/federator/README.md @@ -7,7 +7,7 @@ The federators will be the owners of the contracts willing to allow to cross the ## Config Go to /federator/config copy `config.sample.js` file and rename it to `config.js` set mainchain and sidechain to point to the json files of the networks you are suing, for example rsktestnet-kovan.json and kovan.json, `make sure to set the host parameter of those files`. Create the file `federator.key` inside the config folder, and add the private key of the member of the Federation contract. The members of the federation are controled by the MultiSig contract, same that is owner of the Bridge and AllowedTokens contracts. - +You will also need to add an [etherscan api key](https://etherscan.io/myapikey) in this config file. ## Usage Run `npm install` to install the dependencies, make sure you followed the previous config step. Then to start the service run `npm start` which will start a single federator that listen to both networks. Check the logs to see that everything is working properly. @@ -24,14 +24,15 @@ In order to test with multiple federators, ensure they're added as members of th To run the federator using Docker first, go to the /federator/config folder and rename `config.sample.js` to `config.js`. In that file you will dedcide the networks the federate must be listening, for example for the bridge in testnet a federator config.js will look like -```json +```js module.exports = { mainchain: require('./rsktestnet-kovan.json'), sidechain: require('./kovan.json'), runEvery: 1, // In minutes, confirmations: 10,// Number of blocks before processing it, privateKey: require('federator.key'), - storagePath: './db' + storagePath: './db', + etherscanApiKey: '', } ``` diff --git a/federator/config/config.sample.js b/federator/config/config.sample.js index a3c14a7c7..dbd6c7150 100644 --- a/federator/config/config.sample.js +++ b/federator/config/config.sample.js @@ -5,5 +5,6 @@ module.exports = { runEvery: 2, // In minutes, confirmations: 120, // Number of blocks before processing it, if working with ganache set as 0 privateKey: fs.readFileSync(`${__dirname}/federator.key`, 'utf8'), - storagePath: './db' + storagePath: './db', + etherscanApiKey: '', } \ No newline at end of file diff --git a/federator/package-lock.json b/federator/package-lock.json index 7f8bf7f39..8be11b924 100644 --- a/federator/package-lock.json +++ b/federator/package-lock.json @@ -508,28 +508,36 @@ } }, "@ethersproject/signing-key": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.0.4.tgz", - "integrity": "sha512-I6pJoga1IvhtjYK5yXzCjs4ZpxrVbt9ZRAlpEw0SW9UuV020YfJH5EIVEGR2evdRceS3nAQIggqbsXSkP8Y1Dg==", - "requires": { - "@ethersproject/bytes": "^5.0.4", - "@ethersproject/logger": "^5.0.5", - "@ethersproject/properties": "^5.0.3", - "elliptic": "6.5.3" + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.1.0.tgz", + "integrity": "sha512-tE5LFlbmdObG8bY04NpuwPWSRPgEswfxweAI1sH7TbP0ml1elNfqcq7ii/3AvIN05i5U0Pkm3Tf8bramt8MmLw==", + "requires": { + "@ethersproject/bytes": "^5.1.0", + "@ethersproject/logger": "^5.1.0", + "@ethersproject/properties": "^5.1.0", + "bn.js": "^4.4.0", + "elliptic": "6.5.4" }, "dependencies": { - "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "@ethersproject/bytes": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.1.0.tgz", + "integrity": "sha512-sGTxb+LVjFxJcJeUswAIK6ncgOrh3D8c192iEJd7mLr95V6du119rRfYT/b87WPkZ5I3gRBUYIYXtdgCWACe8g==", "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "@ethersproject/logger": "^5.1.0" + } + }, + "@ethersproject/logger": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.1.0.tgz", + "integrity": "sha512-wtUaD1lBX10HBXjjKV9VHCBnTdUaKQnQ2XSET1ezglqLdPdllNOIlLfhyCRqXm5xwcjExVI5ETokOYfjPtaAlw==" + }, + "@ethersproject/properties": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.1.0.tgz", + "integrity": "sha512-519KKTwgmH42AQL3+GFV3SX6khYEfHsvI6v8HYejlkigSDuqttdgVygFTDsGlofNFchhDwuclrxQnD5B0YLNMg==", + "requires": { + "@ethersproject/logger": "^5.1.0" } } } @@ -545,19 +553,95 @@ } }, "@ethersproject/transactions": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.0.3.tgz", - "integrity": "sha512-cqsAAFUQV6iWqfgLL7KCPNfd3pXJPDdYtE6QuBEAIpc7cgbJ7TIDCF/dN+1otfERHJIbjGSNrhh4axKRnSFswg==", - "requires": { - "@ethersproject/address": "^5.0.3", - "@ethersproject/bignumber": "^5.0.6", - "@ethersproject/bytes": "^5.0.4", - "@ethersproject/constants": "^5.0.3", - "@ethersproject/keccak256": "^5.0.3", - "@ethersproject/logger": "^5.0.5", - "@ethersproject/properties": "^5.0.3", - "@ethersproject/rlp": "^5.0.3", - "@ethersproject/signing-key": "^5.0.4" + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.1.1.tgz", + "integrity": "sha512-Nwgbp09ttIVN0OoUBatCXaHxR7grWPHbozJN8v7AXDLrl6nnOIBEMDh+yJTnosSQlFhcyjfTGGN+Mx6R8HdvMw==", + "requires": { + "@ethersproject/address": "^5.1.0", + "@ethersproject/bignumber": "^5.1.0", + "@ethersproject/bytes": "^5.1.0", + "@ethersproject/constants": "^5.1.0", + "@ethersproject/keccak256": "^5.1.0", + "@ethersproject/logger": "^5.1.0", + "@ethersproject/properties": "^5.1.0", + "@ethersproject/rlp": "^5.1.0", + "@ethersproject/signing-key": "^5.1.0" + }, + "dependencies": { + "@ethersproject/address": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.1.0.tgz", + "integrity": "sha512-rfWQR12eHn2cpstCFS4RF7oGjfbkZb0oqep+BfrT+gWEGWG2IowJvIsacPOvzyS1jhNF4MQ4BS59B04Mbovteg==", + "requires": { + "@ethersproject/bignumber": "^5.1.0", + "@ethersproject/bytes": "^5.1.0", + "@ethersproject/keccak256": "^5.1.0", + "@ethersproject/logger": "^5.1.0", + "@ethersproject/rlp": "^5.1.0" + } + }, + "@ethersproject/bignumber": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.1.1.tgz", + "integrity": "sha512-AVz5iqz7+70RIqoQTznsdJ6DOVBYciNlvO+AlQmPTB6ofCvoihI9bQdr6wljsX+d5W7Yc4nyvQvP4JMzg0Agig==", + "requires": { + "@ethersproject/bytes": "^5.1.0", + "@ethersproject/logger": "^5.1.0", + "bn.js": "^4.4.0" + } + }, + "@ethersproject/bytes": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.1.0.tgz", + "integrity": "sha512-sGTxb+LVjFxJcJeUswAIK6ncgOrh3D8c192iEJd7mLr95V6du119rRfYT/b87WPkZ5I3gRBUYIYXtdgCWACe8g==", + "requires": { + "@ethersproject/logger": "^5.1.0" + } + }, + "@ethersproject/constants": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.1.0.tgz", + "integrity": "sha512-0/SuHrxc8R8k+JiLmJymxHJbojUDWBQqO+b+XFdwaP0jGzqC09YDy/CAlSZB6qHsBifY8X3I89HcK/oMqxRdBw==", + "requires": { + "@ethersproject/bignumber": "^5.1.0" + } + }, + "@ethersproject/keccak256": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.1.0.tgz", + "integrity": "sha512-vrTB1W6AEYoadww5c9UyVJ2YcSiyIUTNDRccZIgwTmFFoSHwBtcvG1hqy9RzJ1T0bMdATbM9Hfx2mJ6H0i7Hig==", + "requires": { + "@ethersproject/bytes": "^5.1.0", + "js-sha3": "0.5.7" + } + }, + "@ethersproject/logger": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.1.0.tgz", + "integrity": "sha512-wtUaD1lBX10HBXjjKV9VHCBnTdUaKQnQ2XSET1ezglqLdPdllNOIlLfhyCRqXm5xwcjExVI5ETokOYfjPtaAlw==" + }, + "@ethersproject/properties": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.1.0.tgz", + "integrity": "sha512-519KKTwgmH42AQL3+GFV3SX6khYEfHsvI6v8HYejlkigSDuqttdgVygFTDsGlofNFchhDwuclrxQnD5B0YLNMg==", + "requires": { + "@ethersproject/logger": "^5.1.0" + } + }, + "@ethersproject/rlp": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.1.0.tgz", + "integrity": "sha512-vDTyHIwNPrecy55gKGZ47eJZhBm8LLBxihzi5ou+zrSvYTpkSTWRcKUlXFDFQVwfWB+P5PGyERAdiDEI76clxw==", + "requires": { + "@ethersproject/bytes": "^5.1.0", + "@ethersproject/logger": "^5.1.0" + } + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + } } }, "@istanbuljs/load-nyc-config": { @@ -1153,6 +1237,14 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" }, + "axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, "babel-jest": { "version": "26.3.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.3.0.tgz", @@ -1469,27 +1561,6 @@ "version": "5.1.3", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==" - }, - "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" - } - } } } }, @@ -1867,17 +1938,24 @@ }, "dependencies": { "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } } } } @@ -2182,6 +2260,27 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, "emittery": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.1.tgz", @@ -2322,17 +2421,24 @@ }, "dependencies": { "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } } } } @@ -2775,6 +2881,11 @@ "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==" }, + "follow-redirects": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.0.tgz", + "integrity": "sha512-0vRwd7RKQBTt+mgu87mtYeofLFZpTas2S9zY+jIeuLJMNvudIgF52nr19q40HOwH5RrhWIPuj9puybzSJiRrVg==" + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -5534,17 +5645,24 @@ }, "dependencies": { "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } } }, "nan": { @@ -6727,6 +6845,27 @@ "web3-utils": "1.2.11" }, "dependencies": { + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + }, + "dependencies": { + "bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + } + } + }, "eth-lib": { "version": "0.2.8", "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", @@ -6735,22 +6874,6 @@ "bn.js": "^4.11.6", "elliptic": "^6.4.0", "xhr-request-promise": "^0.1.2" - }, - "dependencies": { - "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - } } }, "ethereumjs-tx": { @@ -6901,6 +7024,20 @@ "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "eth-lib": { "version": "0.2.8", "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.8.tgz", @@ -6909,22 +7046,6 @@ "bn.js": "^4.11.6", "elliptic": "^6.4.0", "xhr-request-promise": "^0.1.2" - }, - "dependencies": { - "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - } - } } } } diff --git a/federator/package.json b/federator/package.json index 8e3a30a53..e5a5d4e10 100644 --- a/federator/package.json +++ b/federator/package.json @@ -18,6 +18,7 @@ "clean": "rm -rf ./db/*" }, "dependencies": { + "axios": "^0.21.1", "ethereumjs-tx": "^1.3.7", "ethereumjs-util": "^6.1.0", "log4js": "^5.0.0", diff --git a/federator/src/lib/Federator.js b/federator/src/lib/Federator.js index 0227b7971..388cee973 100644 --- a/federator/src/lib/Federator.js +++ b/federator/src/lib/Federator.js @@ -63,10 +63,10 @@ module.exports = class Federator { if (!fs.existsSync(this.config.storagePath)) { fs.mkdirSync(this.config.storagePath); } - let originalFromBlock = this.config.mainchain.fromBlock || 0; + let originalFromBlock = parseInt(this.config.mainchain.fromBlock) || 0; let fromBlock = null; try { - fromBlock = fs.readFileSync(this.lastBlockPath, 'utf8'); + fromBlock = parseInt(fs.readFileSync(this.lastBlockPath, 'utf8')); } catch(err) { fromBlock = originalFromBlock; } @@ -77,7 +77,7 @@ module.exports = class Federator { this.logger.warn(`Current chain Height ${toBlock} is the same or lesser than the last block processed ${fromBlock}`); return false; } - fromBlock = parseInt(fromBlock)+1; + fromBlock = fromBlock + 1; this.logger.debug('Running from Block', fromBlock); const recordsPerPage = 1000; @@ -163,7 +163,8 @@ module.exports = class Federator { this.logger.debug(`Block: ${log.blockHash} Tx: ${log.transactionHash} token: ${symbol} was already processed`); } } - this._saveProgress(this.lastBlockPath, toBlock); + + this._saveProgress(this.lastBlockPath, toBlock.toString()); return true; } catch (err) { diff --git a/federator/src/lib/TransactionSender.js b/federator/src/lib/TransactionSender.js index d2288e805..052fab1cd 100644 --- a/federator/src/lib/TransactionSender.js +++ b/federator/src/lib/TransactionSender.js @@ -4,6 +4,7 @@ const ethUtils = require('ethereumjs-util'); const utils = require('./utils'); const CustomError = require('./CustomError'); const fs = require('fs'); +const axios = require('axios'); module.exports = class TransactionSender { constructor(client, logger, config) { @@ -11,6 +12,7 @@ module.exports = class TransactionSender { this.logger = logger; this.chainId = null; this.manuallyCheck = `${config.storagePath || __dirname}/manuallyCheck.txt`; + this.etherscanApiKey = config.etherscanApiKey; } async getNonce(address) { @@ -24,9 +26,9 @@ module.exports = class TransactionSender { return `0x${Math.ceil(parseInt(number)).toString(16)}`; } - async getGasPrice(chainId) { - chainId = parseInt(chainId) - if(chainId>= 30 && chainId <=33) { + async getGasPrice() { + const chainId = await this.getChainId(); + if (chainId >= 30 && chainId <= 33) { return this.getRskGasPrice(); } return this.getEthGasPrice(); @@ -39,8 +41,48 @@ module.exports = class TransactionSender { } async getEthGasPrice() { + const chainId = await this.getChainId(); const gasPrice = parseInt(await this.client.eth.getGasPrice()); - return gasPrice <= 1 ? 1: Math.round(gasPrice * 1.5); + let useGasPrice = gasPrice <= 1 ? 1: Math.round(gasPrice * 1.5); + if (chainId == 1) { + const data = { + module: 'gastracker', + action: 'gasoracle' + } + const response = await this.useEtherscanApi(data); + const gasOraclePrice = response.result; + const proposeGasPrice = parseInt(this.client.utils.toWei(gasOraclePrice.ProposeGasPrice, 'gwei')); + const fastGasPrice = parseInt(this.client.utils.toWei(gasOraclePrice.FastGasPrice, 'gwei')); + const semiFastGasPrice = Math.round(proposeGasPrice + (fastGasPrice - proposeGasPrice)/2); + if (semiFastGasPrice >= gasPrice && useGasPrice >= semiFastGasPrice) { + // If semiFastGasPrice is cheaper than gasPrice x1.5 use semiFastGasPrice + // we check that semiFastGasPrice is bigger than gasPrice to avoid posible attacks and API errors + this.logger.info('gasPrice', gasPrice,'useGasPrice', useGasPrice); + this.logger.info('gasOraclePrice', gasOraclePrice); + this.logger.debug('useGasPrice >= semiFastGasPrice, we will use', semiFastGasPrice); + return semiFastGasPrice; + } + if (useGasPrice <= 25000000000) { + // Currengly when we restart an ethereum node the eth_getPrice is given values that are lower than the network + // Usually around 9 GWei or 15 GWei that's why we set the limit in 25 GWei + // When this happens we will use the gas price provided by etherscan + this.logger.info('gasPrice', gasPrice,'useGasPrice', useGasPrice); + this.logger.info('gasOraclePrice', gasOraclePrice); + this.logger.debug('useGasPrice <= 25000000000, we will use', semiFastGasPrice); + return semiFastGasPrice; + } + if (proposeGasPrice >= gasPrice && proposeGasPrice >= useGasPrice && proposeGasPrice < (useGasPrice * 5)) { + // if useGasPrice is lower than proposeGasPrice the transaction will probably get stucked + // we add a control in case proposeGasPrice is way high + // Try to use semiFastGasPrice if the value is too high, use proposeGasPrice and add 2 Gwei to help avoid gas spikes + const recomendedGas = semiFastGasPrice < (useGasPrice * 5) ? semiFastGasPrice : proposeGasPrice + 2000000000; + this.logger.info('gasPrice', gasPrice,'useGasPrice', useGasPrice); + this.logger.info('gasOraclePrice', gasOraclePrice); + this.logger.debug('proposeGasPrice >= useGasPrice, we will use', recomendedGas); + return recomendedGas; + } + } + return useGasPrice; } async getRskGasPrice() { @@ -49,10 +91,14 @@ module.exports = class TransactionSender { return gasPrice <= 1 ? 1: Math.round(gasPrice * 1.05); } + async getChainId() { + return parseInt(this.chainId || await this.client.eth.net.getId()); + } + async createRawTransaction(from, to, data, value) { const nonce = await this.getNonce(from); - const chainId = this.chainId || await this.client.eth.net.getId(); - const gasPrice = await this.getGasPrice(chainId); + const chainId = await this.getChainId(); + const gasPrice = await this.getGasPrice(); let rawTx = { chainId: chainId, gasPrice: this.numberToHexString(gasPrice), @@ -87,19 +133,58 @@ module.exports = class TransactionSender { return address; } + async useEtherscanApi(data) { + const chainId = await this.getChainId(); + if(chainId !=1 && chainId != 42) + throw new Error(`ChainId:${chainId} can't use Etherescan API`); + + const url = chainId == 1 ? 'https://api.etherscan.io/api' : 'https://api-kovan.etherscan.io/api'; + + const params = new URLSearchParams() + params.append('apikey', this.etherscanApiKey); + for (const property in data) { + params.append(property, data[property]); + } + + const config = { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }; + const response = await axios.post(url, params, config); + + if (response.data.status == 0) { + throw new Error(`Etherscan API:${url} data:${JSON.stringify(data)} message:${response.data.message} result:${response.data.result}`); + } + return response.data; + } + async sendTransaction(to, data, value, privateKey) { const stack = new Error().stack; - var from = await this.getAddress(privateKey); - let rawTx = await this.createRawTransaction(from, to, data, value); + const chainId = await this.getChainId(); let txHash; let error = ''; let errorInfo = ''; try { + var from = await this.getAddress(privateKey); + let rawTx = await this.createRawTransaction(from, to, data, value); let receipt; if (privateKey && privateKey.length) { let signedTx = this.signRawTransaction(rawTx, privateKey); const serializedTx = ethUtils.bufferToHex(signedTx.serialize()); - receipt = await this.client.eth.sendSignedTransaction(serializedTx).once('transactionHash', hash => txHash = hash); + receipt = await this.client.eth.sendSignedTransaction(serializedTx).once('transactionHash', async (hash) => { + txHash = hash; + if (chainId == 1) { + // send a POST request to Etherscan, we broadcast the same transaction as GETH is not working correclty + // see https://github.com/ethereum/go-ethereum/issues/22308 + const data = { + module: 'proxy', + action: 'eth_sendRawTransaction', + hex: serializedTx, + } + await this.useEtherscanApi(data); + } + }); } else { //If no private key provided we use personal (personal is only for testing) delete rawTx.r; @@ -108,19 +193,19 @@ module.exports = class TransactionSender { receipt = await this.client.eth.sendTransaction(rawTx).once('transactionHash', hash => txHash = hash); } if(receipt.status == 1) { - this.logger.info(`Transaction Successful txHash:${receipt.transactionHash} blockNumber:${receipt.blockNumber}`); + this.logger.info(`Transaction Successful chain:${chainId} txHash:${receipt.transactionHash} blockNumber:${receipt.blockNumber}`); return receipt; } - error = 'Transaction Receipt Status Failed'; + error = `Transaction Receipt Status Failed chain:${chainId}`; errorInfo = receipt; } catch(err) { if (err.message.indexOf('it might still be mined') > 0) { this.logger.warn(`Transaction was not mined within 750 seconds, please make sure your transaction was properly sent. Be aware that - it might still be mined. transactionHash:${txHash}`); - fs.appendFileSync(this.manuallyCheck, `transactionHash:${txHash} to:${to} data:${data}\n`); + it might still be mined. Chain:${chainId} transactionHash:${txHash}`); + fs.appendFileSync(this.manuallyCheck, `chain:${chainId} transactionHash:${txHash} to:${to} data:${data}\n`); return { transactionHash: txHash }; } - error = `Send Signed Transaction Failed TxHash:${txHash}`; + error = `Send Signed Transaction to chain:${chainId} Failed TxHash:${txHash}`; errorInfo = err; } this.logger.error(error, errorInfo); diff --git a/federator/src/main.js b/federator/src/main.js index 5d89c517e..479f31b53 100644 --- a/federator/src/main.js +++ b/federator/src/main.js @@ -13,11 +13,16 @@ const logger = log4js.getLogger('Federators'); logger.info('RSK Host', config.mainchain.host); logger.info('ETH Host', config.sidechain.host); -if(!config.mainchain || !config.sidechain) { +if (!config.mainchain || !config.sidechain) { logger.error('Mainchain and Sidechain configuration are required'); process.exit(); } +if (!config.etherscanApiKey) { + logger.error('Etherscan API configuration is required'); + process.exit(); +} + const mainFederator = new Federator(config, log4js.getLogger('MAIN-FEDERATOR')); const sideFederator = new Federator({ ...config, From ff5920f33c70b70ba69adc2402ed45ab111eb997 Mon Sep 17 00:00:00 2001 From: pedro Date: Fri, 7 May 2021 10:31:59 -0300 Subject: [PATCH 2/4] Fix typos --- federator/src/lib/TransactionSender.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/federator/src/lib/TransactionSender.js b/federator/src/lib/TransactionSender.js index 052fab1cd..e2b2707a3 100644 --- a/federator/src/lib/TransactionSender.js +++ b/federator/src/lib/TransactionSender.js @@ -63,7 +63,7 @@ module.exports = class TransactionSender { return semiFastGasPrice; } if (useGasPrice <= 25000000000) { - // Currengly when we restart an ethereum node the eth_getPrice is given values that are lower than the network + // Currently when we restart an ethereum node the eth_getPrice is given values that are lower than the network // Usually around 9 GWei or 15 GWei that's why we set the limit in 25 GWei // When this happens we will use the gas price provided by etherscan this.logger.info('gasPrice', gasPrice,'useGasPrice', useGasPrice); @@ -75,11 +75,11 @@ module.exports = class TransactionSender { // if useGasPrice is lower than proposeGasPrice the transaction will probably get stucked // we add a control in case proposeGasPrice is way high // Try to use semiFastGasPrice if the value is too high, use proposeGasPrice and add 2 Gwei to help avoid gas spikes - const recomendedGas = semiFastGasPrice < (useGasPrice * 5) ? semiFastGasPrice : proposeGasPrice + 2000000000; + const recommendedGas = semiFastGasPrice < (useGasPrice * 5) ? semiFastGasPrice : proposeGasPrice + 2000000000; this.logger.info('gasPrice', gasPrice,'useGasPrice', useGasPrice); this.logger.info('gasOraclePrice', gasOraclePrice); - this.logger.debug('proposeGasPrice >= useGasPrice, we will use', recomendedGas); - return recomendedGas; + this.logger.debug('proposeGasPrice >= useGasPrice, we will use', recommendedGas); + return recommendedGas; } } return useGasPrice; From f6a7f3e44802588512009532ca619bf3bd968c0c Mon Sep 17 00:00:00 2001 From: pedro Date: Tue, 11 May 2021 18:09:16 -0300 Subject: [PATCH 3/4] Use FastGas price instead of SemiFast --- federator/src/lib/TransactionSender.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/federator/src/lib/TransactionSender.js b/federator/src/lib/TransactionSender.js index e2b2707a3..4b7bfc7d5 100644 --- a/federator/src/lib/TransactionSender.js +++ b/federator/src/lib/TransactionSender.js @@ -53,14 +53,13 @@ module.exports = class TransactionSender { const gasOraclePrice = response.result; const proposeGasPrice = parseInt(this.client.utils.toWei(gasOraclePrice.ProposeGasPrice, 'gwei')); const fastGasPrice = parseInt(this.client.utils.toWei(gasOraclePrice.FastGasPrice, 'gwei')); - const semiFastGasPrice = Math.round(proposeGasPrice + (fastGasPrice - proposeGasPrice)/2); - if (semiFastGasPrice >= gasPrice && useGasPrice >= semiFastGasPrice) { - // If semiFastGasPrice is cheaper than gasPrice x1.5 use semiFastGasPrice - // we check that semiFastGasPrice is bigger than gasPrice to avoid posible attacks and API errors + if (fastGasPrice >= gasPrice && useGasPrice >= fastGasPrice) { + // If fastGasPrice is cheaper than gasPrice x1.5 use fastGasPrice + // we check that fastGasPrice is bigger than gasPrice to avoid posible attacks and API errors this.logger.info('gasPrice', gasPrice,'useGasPrice', useGasPrice); this.logger.info('gasOraclePrice', gasOraclePrice); - this.logger.debug('useGasPrice >= semiFastGasPrice, we will use', semiFastGasPrice); - return semiFastGasPrice; + this.logger.debug('useGasPrice >= fastGasPrice, we will use', fastGasPrice); + return fastGasPrice; } if (useGasPrice <= 25000000000) { // Currently when we restart an ethereum node the eth_getPrice is given values that are lower than the network @@ -68,14 +67,14 @@ module.exports = class TransactionSender { // When this happens we will use the gas price provided by etherscan this.logger.info('gasPrice', gasPrice,'useGasPrice', useGasPrice); this.logger.info('gasOraclePrice', gasOraclePrice); - this.logger.debug('useGasPrice <= 25000000000, we will use', semiFastGasPrice); - return semiFastGasPrice; + this.logger.debug('useGasPrice <= 25000000000, we will use', fastGasPrice); + return fastGasPrice; } if (proposeGasPrice >= gasPrice && proposeGasPrice >= useGasPrice && proposeGasPrice < (useGasPrice * 5)) { // if useGasPrice is lower than proposeGasPrice the transaction will probably get stucked // we add a control in case proposeGasPrice is way high - // Try to use semiFastGasPrice if the value is too high, use proposeGasPrice and add 2 Gwei to help avoid gas spikes - const recommendedGas = semiFastGasPrice < (useGasPrice * 5) ? semiFastGasPrice : proposeGasPrice + 2000000000; + // Try to use fastGasPrice if the value is too high, use proposeGasPrice and add 2 Gwei to help avoid gas spikes + const recommendedGas = fastGasPrice < (useGasPrice * 5) ? fastGasPrice : proposeGasPrice + 2000000000; this.logger.info('gasPrice', gasPrice,'useGasPrice', useGasPrice); this.logger.info('gasOraclePrice', gasOraclePrice); this.logger.debug('proposeGasPrice >= useGasPrice, we will use', recommendedGas); @@ -166,7 +165,7 @@ module.exports = class TransactionSender { let error = ''; let errorInfo = ''; try { - var from = await this.getAddress(privateKey); + let from = await this.getAddress(privateKey); let rawTx = await this.createRawTransaction(from, to, data, value); let receipt; if (privateKey && privateKey.length) { From 2dbdb1ac25566ebf6a9598504d81e3da2fef4989 Mon Sep 17 00:00:00 2001 From: pedro Date: Tue, 18 May 2021 17:53:46 -0300 Subject: [PATCH 4/4] Add a 1.3% margin to avoid gas spikes --- federator/src/lib/TransactionSender.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/federator/src/lib/TransactionSender.js b/federator/src/lib/TransactionSender.js index 4b7bfc7d5..cb98b692a 100644 --- a/federator/src/lib/TransactionSender.js +++ b/federator/src/lib/TransactionSender.js @@ -53,13 +53,15 @@ module.exports = class TransactionSender { const gasOraclePrice = response.result; const proposeGasPrice = parseInt(this.client.utils.toWei(gasOraclePrice.ProposeGasPrice, 'gwei')); const fastGasPrice = parseInt(this.client.utils.toWei(gasOraclePrice.FastGasPrice, 'gwei')); + // Add a 1.3% margin to avoid gas spikes as even fast gas price is not enough + const fastGasPricePlus = Math.ceil(fastGasPrice * 1.013); if (fastGasPrice >= gasPrice && useGasPrice >= fastGasPrice) { // If fastGasPrice is cheaper than gasPrice x1.5 use fastGasPrice // we check that fastGasPrice is bigger than gasPrice to avoid posible attacks and API errors this.logger.info('gasPrice', gasPrice,'useGasPrice', useGasPrice); this.logger.info('gasOraclePrice', gasOraclePrice); - this.logger.debug('useGasPrice >= fastGasPrice, we will use', fastGasPrice); - return fastGasPrice; + this.logger.debug('useGasPrice >= fastGasPrice, we will use', fastGasPricePlus); + return fastGasPricePlus; } if (useGasPrice <= 25000000000) { // Currently when we restart an ethereum node the eth_getPrice is given values that are lower than the network @@ -67,14 +69,14 @@ module.exports = class TransactionSender { // When this happens we will use the gas price provided by etherscan this.logger.info('gasPrice', gasPrice,'useGasPrice', useGasPrice); this.logger.info('gasOraclePrice', gasOraclePrice); - this.logger.debug('useGasPrice <= 25000000000, we will use', fastGasPrice); - return fastGasPrice; + this.logger.debug('useGasPrice <= 25000000000, we will use', fastGasPricePlus); + return fastGasPricePlus; } if (proposeGasPrice >= gasPrice && proposeGasPrice >= useGasPrice && proposeGasPrice < (useGasPrice * 5)) { // if useGasPrice is lower than proposeGasPrice the transaction will probably get stucked // we add a control in case proposeGasPrice is way high // Try to use fastGasPrice if the value is too high, use proposeGasPrice and add 2 Gwei to help avoid gas spikes - const recommendedGas = fastGasPrice < (useGasPrice * 5) ? fastGasPrice : proposeGasPrice + 2000000000; + const recommendedGas = fastGasPrice < (useGasPrice * 5) ? fastGasPricePlus : proposeGasPrice + 5000000000; this.logger.info('gasPrice', gasPrice,'useGasPrice', useGasPrice); this.logger.info('gasOraclePrice', gasOraclePrice); this.logger.debug('proposeGasPrice >= useGasPrice, we will use', recommendedGas); @@ -134,7 +136,7 @@ module.exports = class TransactionSender { async useEtherscanApi(data) { const chainId = await this.getChainId(); - if(chainId !=1 && chainId != 42) + if(chainId != 1 && chainId != 42) throw new Error(`ChainId:${chainId} can't use Etherescan API`); const url = chainId == 1 ? 'https://api.etherscan.io/api' : 'https://api-kovan.etherscan.io/api';