From c2534570cc6dd3f4a22888a305d084f234d6fe33 Mon Sep 17 00:00:00 2001 From: Richard Ramos Date: Tue, 18 Sep 2018 09:11:53 -0400 Subject: [PATCH] Logging and Caching --- gas-relayer/config/config.js | 4 +- gas-relayer/config/config.testnet.js | 7 +- gas-relayer/package-lock.json | 219 ++++++++++++++++-- gas-relayer/package.json | 5 +- gas-relayer/src/contract-settings.js | 7 +- gas-relayer/src/message-processor.js | 14 +- gas-relayer/src/service.js | 70 +++--- .../src/strategy/AvailabilityStrategy.js | 17 +- gas-relayer/src/strategy/BaseStrategy.js | 2 +- gas-relayer/src/strategy/IdentityStrategy.js | 17 +- gas-relayer/src/strategy/SNTStrategy.js | 17 +- 11 files changed, 312 insertions(+), 67 deletions(-) diff --git a/gas-relayer/config/config.js b/gas-relayer/config/config.js index f83c279..1b851ad 100644 --- a/gas-relayer/config/config.js +++ b/gas-relayer/config/config.js @@ -26,12 +26,14 @@ module.exports = { "0x0000000000000000000000000000000000000000": { "name": "Ethereum", "symbol": "ETH", - "minAcceptedRate": 1 + "minAcceptedRate": 1, + "refreshPricePeriod": 60000 }, "%STTAddress%": { "name": "Status Test Token", "symbol": "SNT", "minAcceptedRate": 0.0001500, + "refreshPricePeriod": 60000, "pricePlugin": "../plugins/token-utils.js" } }, diff --git a/gas-relayer/config/config.testnet.js b/gas-relayer/config/config.testnet.js index e639317..de7e172 100644 --- a/gas-relayer/config/config.testnet.js +++ b/gas-relayer/config/config.testnet.js @@ -26,14 +26,15 @@ module.exports = { "0x0000000000000000000000000000000000000000": { "name": "Ethereum", "symbol": "ETH", - "minAcceptedRate": 1 - + "minAcceptedRate": 1, + "refreshPricePeriod": 60000 }, "0x121a430A73Fc13e2D6d4a9dc3E943de647c30f8f": { "name": "Status Gas Relayer Test Token", "symbol": "SNT", "minAcceptedRate": 0.0001500, - "pricePlugin": "../plugins/token-utils.js" + "pricePlugin": "../plugins/token-utils.js", + "refreshPricePeriod": 60000 } }, diff --git a/gas-relayer/package-lock.json b/gas-relayer/package-lock.json index 8ec4077..7cfaab3 100644 --- a/gas-relayer/package-lock.json +++ b/gas-relayer/package-lock.json @@ -191,7 +191,6 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, "requires": { "color-convert": "1.9.3" } @@ -259,6 +258,14 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, + "async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "requires": { + "lodash": "4.17.10" + } + }, "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", @@ -884,7 +891,6 @@ "version": "2.4.1", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, "requires": { "ansi-styles": "3.2.1", "escape-string-regexp": "1.0.5", @@ -897,6 +903,11 @@ "integrity": "sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I=", "dev": true }, + "ci-info": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.5.1.tgz", + "integrity": "sha512-fKFIKXaYiL1exImwJ0AhR/6jxFPSKQBk2ayV5NiNoruUs2+rxC2kNw0EG+1Z9dugZRdCrppskQ8DN2cyaUM1Hw==" + }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -932,11 +943,19 @@ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "requires": { + "color-convert": "1.9.3", + "color-string": "1.5.3" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, "requires": { "color-name": "1.1.3" } @@ -944,8 +963,35 @@ "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "requires": { + "color-name": "1.1.3", + "simple-swizzle": "0.2.2" + } + }, + "colornames": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", + "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=" + }, + "colors": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.2.tgz", + "integrity": "sha512-rhP0JSBGYvpcNQj4s5AdShMeE5ahMop96cTeDl/v9qQQm2fYClE2QXZRi8wLzc+GmXSxdIqqbOIAhyObEXDbfQ==" + }, + "colorspace": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.1.tgz", + "integrity": "sha512-pI3btWyiuz7Ken0BWh9Elzsmv2bM9AhA7psXib4anUXy/orfZ/E0MbQwhSOG/9L8hLlalqrU0UhOuqxW1YjmVw==", + "requires": { + "color": "3.0.0", + "text-hex": "1.0.0" + } }, "combined-stream": { "version": "1.0.6", @@ -980,6 +1026,17 @@ "typedarray": "0.0.6" } }, + "consola": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-1.4.3.tgz", + "integrity": "sha512-PIbVeO9JVVeJ9eY2n8PrkL+hXBGnmaD5x4yJxp2K9nWR7zgtAzRn7rmWxu/d0Iyyr92v8s5AM0qax6xQZ5rSeQ==", + "requires": { + "chalk": "2.4.1", + "figures": "2.0.0", + "lodash": "4.17.10", + "std-env": "1.3.1" + } + }, "contains-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", @@ -1254,6 +1311,16 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "diagnostics": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", + "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", + "requires": { + "colorspace": "1.1.1", + "enabled": "1.0.2", + "kuler": "1.0.0" + } + }, "diffie-hellman": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", @@ -1312,6 +1379,14 @@ "minimalistic-crypto-utils": "1.0.1" } }, + "enabled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", + "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", + "requires": { + "env-variable": "0.0.4" + } + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", @@ -1325,6 +1400,11 @@ "once": "1.4.0" } }, + "env-variable": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.4.tgz", + "integrity": "sha512-+jpGxSWG4vr6gVxUHOc4p+ilPnql7NzZxOZBxNldsKGjCF+97df3CbuX7XMaDa5oAVkKQj4rKp38rYdC4VcpDg==" + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -1342,8 +1422,7 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "eslint": { "version": "4.19.1", @@ -1789,6 +1868,11 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fast-safe-stringify": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz", + "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==" + }, "fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -1797,11 +1881,15 @@ "pend": "1.2.0" } }, + "fecha": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", + "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" + }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", - "dev": true, "requires": { "escape-string-regexp": "1.0.5" } @@ -2097,8 +2185,7 @@ "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "has-symbol-support-x": { "version": "1.4.2", @@ -2290,6 +2377,14 @@ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" }, + "is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "requires": { + "ci-info": "1.5.1" + } + }, "is-fullwidth-code-point": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", @@ -2484,6 +2579,14 @@ "sha3": "1.2.2" } }, + "kuler": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.0.tgz", + "integrity": "sha512-oyy6pu/yWRjiVfCoJebNUKFL061sNtrs9ejKTbirIwY3oiHmENVCSkHhxDV85Dkm7JYR/czMCBeoM87WilTdSg==", + "requires": { + "colornames": "1.1.1" + } + }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -2527,8 +2630,26 @@ "lodash": { "version": "4.17.10", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", - "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==", - "dev": true + "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" + }, + "logform": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/logform/-/logform-1.9.1.tgz", + "integrity": "sha512-ZHrZE8VSf7K3xKxJiQ1aoTBp2yK+cEbFcgarsjzI3nt3nE/3O0heNSppoOQMUJVMZo/xiVwCxiXIabaZApsKNQ==", + "requires": { + "colors": "1.3.2", + "fast-safe-stringify": "2.0.6", + "fecha": "2.3.3", + "ms": "2.1.1", + "triple-beam": "1.3.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } }, "loose-envify": { "version": "1.4.0", @@ -2583,6 +2704,11 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "memory-cache": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", + "integrity": "sha1-eJCwHVLADI68nVM+H46xfjA0hxo=" + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -2792,6 +2918,11 @@ "wrappy": "1.0.2" } }, + "one-time": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", + "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=" + }, "onetime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", @@ -3433,6 +3564,21 @@ "simple-concat": "1.0.0" } }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "0.3.2" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + } + } + }, "slice-ansi": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", @@ -3510,11 +3656,24 @@ "tweetnacl": "0.14.5" } }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, + "std-env": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-1.3.1.tgz", + "integrity": "sha512-KI2F2pPJpd3lHjng+QLezu0eq+QDtXcv1um016mhOPAJFHKL+09ykK5PUBWta2pZDC8BVV0VPya08A15bUXSLQ==", + "requires": { + "is-ci": "1.2.1" + } + }, "strict-uri-encode": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", @@ -3596,7 +3755,6 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, "requires": { "has-flag": "3.0.0" } @@ -3678,6 +3836,11 @@ } } }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -3750,6 +3913,11 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -4191,6 +4359,31 @@ "isexe": "2.0.0" } }, + "winston": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.1.0.tgz", + "integrity": "sha512-FsQfEE+8YIEeuZEYhHDk5cILo1HOcWkGwvoidLrDgPog0r4bser1lEIOco2dN9zpDJ1M88hfDgZvxe5z4xNcwg==", + "requires": { + "async": "2.6.1", + "diagnostics": "1.1.1", + "is-stream": "1.1.0", + "logform": "1.9.1", + "one-time": "0.0.4", + "readable-stream": "2.3.6", + "stack-trace": "0.0.10", + "triple-beam": "1.3.0", + "winston-transport": "4.2.0" + } + }, + "winston-transport": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.2.0.tgz", + "integrity": "sha512-0R1bvFqxSlK/ZKTH86nymOuKv/cT1PQBMuDdA7k7f0S9fM44dNH6bXnuxwXPrN8lefJgtZq08BKdyZ0DZIy/rg==", + "requires": { + "readable-stream": "2.3.6", + "triple-beam": "1.3.0" + } + }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", diff --git a/gas-relayer/package.json b/gas-relayer/package.json index 501f006..b7f854b 100644 --- a/gas-relayer/package.json +++ b/gas-relayer/package.json @@ -22,9 +22,12 @@ }, "dependencies": { "axios": "^0.18.0", + "consola": "^1.4.3", "daemonize2": "^0.4.2", "ganache-cli": "^6.1.0", "jsum": "^0.1.4", - "web3": "^1.0.0-beta.33" + "memory-cache": "^0.2.0", + "web3": "^1.0.0-beta.33", + "winston": "^3.1.0" } } diff --git a/gas-relayer/src/contract-settings.js b/gas-relayer/src/contract-settings.js index 03a8ca9..16468d6 100644 --- a/gas-relayer/src/contract-settings.js +++ b/gas-relayer/src/contract-settings.js @@ -8,11 +8,12 @@ class ContractSettings { * @param {object} web3 - Web3 object already configured * @param {object} eventEmitter - Event Emitter */ - constructor(config, web3, eventEmitter){ + constructor(config, web3, eventEmitter, logger){ this.tokens = config.tokens; this.topics = []; this.contracts = config.contracts; this.config = config; + this.logger = logger; this.web3 = web3; this.events = eventEmitter; @@ -107,8 +108,8 @@ class ContractSettings { this.pendingToLoad--; if(this.pendingToLoad == 0) this.events.emit("setup:complete", this); } catch(err) { - console.error("Invalid contract for " + topicName); - console.error(err); + this.logger.error("Invalid contract for " + topicName); + this.logger.error(err); process.exit(); } } diff --git a/gas-relayer/src/message-processor.js b/gas-relayer/src/message-processor.js index 557a812..9a58976 100644 --- a/gas-relayer/src/message-processor.js +++ b/gas-relayer/src/message-processor.js @@ -9,11 +9,13 @@ class MessageProcessor { * @param {object} web3 - Web3 object already configured * @param {object} events - Event emitter */ - constructor(config, settings, web3, events){ + constructor(config, settings, web3, events, logger, cache){ this.config = config; this.settings = settings; this.web3 = web3; this.events = events; + this.logger = logger; + this.cache = cache; } /** @@ -23,7 +25,7 @@ class MessageProcessor { * @returns {object} State of validation */ async _validateInput(contract, input){ - console.info("Processing '%s' request to contract: %s", input.action, input.contract); + this.logger.info("Processing '" + input.action + "' request to contract: " + input.contract); if(contract == undefined){ return {success: false, message: 'Unknown contract'}; @@ -71,9 +73,9 @@ class MessageProcessor { if(strategy || contract.strategy){ let validationResult; if(strategy){ - validationResult = await strategy.execute(input, reply); + validationResult = await strategy.execute(input, this.cache); } else { - validationResult = await contract.strategy.execute(input, reply); + validationResult = await contract.strategy.execute(input, this.cache); } if(!validationResult.success){ @@ -114,7 +116,7 @@ class MessageProcessor { if(nodeBalance < p.gas){ reply("Relayer unavailable"); - console.error("Relayer doesn't have enough gas to process trx: %s, required %s", nodeBalance, p.gas); + this.logger.error("Relayer doesn't have enough gas to process trx: " + nodeBalance + ", required " + p.gas); this.events.emit('exit'); } else { try { @@ -129,7 +131,7 @@ class MessageProcessor { } catch(err){ reply("Couldn't mine transaction: " + err.message); // TODO log this? - console.error(err); + this.logger.error(err); } } } diff --git a/gas-relayer/src/service.js b/gas-relayer/src/service.js index 63f1039..23d5408 100644 --- a/gas-relayer/src/service.js +++ b/gas-relayer/src/service.js @@ -4,9 +4,24 @@ const config = require('../config/config.js'); const ContractSettings = require('./contract-settings'); const MessageProcessor = require('./message-processor'); const JSum = require('jsum'); +const logger = require('consola'); +const winston = require('winston'); +var cache = require('memory-cache'); + +// Setting up logging +const wLogger = winston.createLogger({ + level: 'info', + format: winston.format.simple(), + transports: [ + new winston.transports.Console(), + new winston.transports.File({filename: 'gas-relayer.log'}) + ] +}); +logger.clear().add(new logger.WinstonReporter(wLogger)); -console.info("Starting..."); +// Service Init +logger.info("Starting..."); const events = new EventEmitter(); // Web3 Connection @@ -17,14 +32,14 @@ const web3 = new Web3(wsProvider); web3.eth.net.isListening() .then(() => events.emit('web3:connected', connectionURL)) .catch(error => { - console.error(error); + logger.error(error); process.exit(); }); events.on('web3:connected', connURL => { - console.info("Connected to '%s'", connURL); - let settings = new ContractSettings(config, web3, events); + logger.info("Connected to '" + connURL + "'"); + let settings = new ContractSettings(config, web3, events, logger); settings.process(); }); @@ -37,9 +52,9 @@ const shhOptions = { const verifyBalance = async (exitSubs) => { const nodeBalance = await web3.eth.getBalance(config.node.blockchain.account); if(web3.utils.toBN(nodeBalance).lte(web3.utils.toBN(100000))){ // TODO: tune minimum amount required for transactions - console.log("Not enough balance available for processing transactions"); - console.log("> Account: %s", config.node.blockchain.account); - console.log("> Balance: %s", nodeBalance); + logger.info("Not enough balance available for processing transactions"); + logger.info("> Account: " + config.node.blockchain.account); + logger.info("> Balance: " + nodeBalance); if(exitSubs){ web3.shh.clearSubscriptions(); @@ -51,7 +66,7 @@ const verifyBalance = async (exitSubs) => { events.on('exit', () => { web3.shh.clearSubscriptions(); - console.log("Closing service..."); + logger.info("Closing service..."); process.exit(0); }); @@ -67,11 +82,11 @@ events.on('setup:complete', async (settings) => { // Listening to whisper // Individual subscriptions due to https://github.com/ethereum/web3.js/issues/1361 // once this is fixed, we'll be able to use an array of topics and a single subs for symkey and a single subs for privKey - console.info(`Sym Key: ${config.node.whisper.symKey}`); - console.info(`Relayer Public Key: ${pubKey}`); - console.info("Topics Available:"); + logger.info(`Sym Key: ${config.node.whisper.symKey}`); + logger.info(`Relayer Public Key: ${pubKey}`); + logger.info("Topics Available:"); for(let contract in settings.contracts) { - console.info("- %s: %s [%s]", settings.getContractByTopic(contract).name, contract, Object.keys(settings.getContractByTopic(contract).allowedFunctions).join(', ')); + logger.info("- " + settings.getContractByTopic(contract).name + ": " + contract + " [" + (Object.keys(settings.getContractByTopic(contract).allowedFunctions).join(', ')) + "]"); shhOptions.topics = [contract]; // Listen to public channel - Used for reporting availability @@ -126,7 +141,7 @@ const extractInput = (message) => { obj.gasPrice = parsedObj.gasPrice; } } catch(err){ - console.error("Couldn't parse " + message); + logger.error("Couldn't parse " + message); } return obj; @@ -136,28 +151,29 @@ const extractInput = (message) => { let messagesCheckSum = {}; events.on('server:listen', (shhOptions, settings) => { - let processor = new MessageProcessor(config, settings, web3, events); + let processor = new MessageProcessor(config, settings, web3, events, logger, cache); web3.shh.subscribe('messages', shhOptions, async (error, message) => { if(error){ - console.error(error); + logger.error(error); return; } verifyBalance(true); const input = extractInput(message); - const inputCheckSum = JSum.digest(input, 'SHA256', 'hex'); + const inputCheckSum = JSum.digest({input}, 'SHA256', 'hex'); const reply = replyFunction(message, inputCheckSum); - // TODO: Probably it makes sense to have some small db to store checksums - if(messagesCheckSum[inputCheckSum] && messagesCheckSum[inputCheckSum] + 3600000 > (new Date().getTime())){ + if(cache.get(inputCheckSum)){ reply("Duplicated message received"); } else { let validationResult; switch(input.action){ case 'transaction': - messagesCheckSum[inputCheckSum] = (new Date().getTime()); + + cache.put(inputCheckSum, (new Date().getTime()), 86400000); + processor.processTransaction(settings.getContractByTopic(message.topic), input, reply); @@ -181,26 +197,14 @@ events.on('server:listen', (shhOptions, settings) => { }); }); -// Cleaning old message checksums -const deleteOldChecksums = () => { - for (var key in messagesCheckSum) { - if (messagesCheckSum.hasOwnProperty(key)) { - if(messagesCheckSum[key] + 86400000 < (new Date().getTime())){ - delete messagesCheckSum[key]; - } - } - } -}; - -setInterval(deleteOldChecksums, 3600000); // Daemon helper functions process.on("uncaughtException", function(err) { // TODO - console.error(err); + logger.error(err); }); process.once("SIGTERM", function() { - console.log("Stopping..."); + logger.info("Stopping..."); }); diff --git a/gas-relayer/src/strategy/AvailabilityStrategy.js b/gas-relayer/src/strategy/AvailabilityStrategy.js index a5ee1f1..54b9c81 100644 --- a/gas-relayer/src/strategy/AvailabilityStrategy.js +++ b/gas-relayer/src/strategy/AvailabilityStrategy.js @@ -11,14 +11,27 @@ class AvailabilityStrategy extends Strategy { * @param {object} input - Object obtained from an 'availability' request. It expects an object with this structure `{contract, address, action, gasToken, gasPrice}` * @returns {object} Status of validation, and minimum price */ - async execute(input){ + async execute(input, cache){ // Verifying if token is allowed const token = this.settings.getToken(input.gasToken); if(token == undefined) return {success: false, message: "Token not allowed"}; // Get Price - const tokenRate = await token.pricePlugin.getRate(); + let tokenRate = cache.get(input.gasToken); + if(tokenRate === null){ + try { + tokenRate = await token.pricePlugin.getRate(); + cache.put(input.gasToken, tokenRate, token.refreshPricePeriod); + } catch (err) { + console.error(err); + return { + success: false, + message: "Token price unavailable" + }; + } + } + const minRate = token.minAcceptedRate; if(tokenRate >= minRate){ // TODO: verify this diff --git a/gas-relayer/src/strategy/BaseStrategy.js b/gas-relayer/src/strategy/BaseStrategy.js index 32259ca..34bec04 100644 --- a/gas-relayer/src/strategy/BaseStrategy.js +++ b/gas-relayer/src/strategy/BaseStrategy.js @@ -90,7 +90,7 @@ class BaseStrategy { } /* - async execute(message, reply){ + async execute(input){ return { success: true, message: "Valid transaction" diff --git a/gas-relayer/src/strategy/IdentityStrategy.js b/gas-relayer/src/strategy/IdentityStrategy.js index f929850..701749d 100644 --- a/gas-relayer/src/strategy/IdentityStrategy.js +++ b/gas-relayer/src/strategy/IdentityStrategy.js @@ -32,7 +32,7 @@ class IdentityStrategy extends Strategy { * @param {object} input - Object obtained from an 'transaction' request. It expects an object with this structure `{contract, address, action, functionName, functionParameters, payload}` * @returns {object} Status of validation and estimated gas */ - async execute(input){ + async execute(input, cache){ if(this.contract.isIdentity){ let validInstance = await this._validateInstance(input); if(!validInstance){ @@ -87,7 +87,20 @@ class IdentityStrategy extends Strategy { } // Get Price - const tokenRate = await token.pricePlugin.getRate(); + let tokenRate = cache.get(input.gasToken); + if(tokenRate === null){ + try { + tokenRate = await token.pricePlugin.getRate(); + cache.put(input.gasToken, tokenRate, token.refreshPricePeriod); + } catch (err) { + console.error(err); + return { + success: false, + message: "Token price unavailable" + }; + } + } + const minRate = token.minAcceptedRate; if(tokenRate < minRate){ // TODO: verify this. Maybe we want to accept a minRate instead of just simply not processing the trx diff --git a/gas-relayer/src/strategy/SNTStrategy.js b/gas-relayer/src/strategy/SNTStrategy.js index 0893a4e..68bde97 100644 --- a/gas-relayer/src/strategy/SNTStrategy.js +++ b/gas-relayer/src/strategy/SNTStrategy.js @@ -14,7 +14,7 @@ class SNTStrategy extends Strategy { * @param {object} input - Object obtained from an 'transaction' request. It expects an object with this structure `{contract, address, action, functionName, functionParameters, payload}` * @returns {object} Status of validation and estimated gas */ - async execute(input){ + async execute(input, cache){ const params = this._obtainParametersFunc(input); // Verifying if token is allowed @@ -30,7 +30,20 @@ class SNTStrategy extends Strategy { }); // Get Price - const tokenRate = await token.pricePlugin.getRate(); + let tokenRate = cache.get(input.gasToken); + if(tokenRate === null){ + try { + tokenRate = await token.pricePlugin.getRate(); + cache.put(input.gasToken, tokenRate, token.refreshPricePeriod); + } catch (err) { + console.error(err); + return { + success: false, + message: "Token price unavailable" + }; + } + } + const minRate = token.minAcceptedRate; if(tokenRate < minRate){ // TODO: verify this. Maybe we want to accept a minRate instead of just simply not processing the trx