From 4032e66be3ab0458fa1b9e18292f0b79d84c6a83 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 20 Oct 2019 11:55:46 -0700 Subject: [PATCH 01/49] Created middleware to retrieve JWT token from header --- package-lock.json | 308 +++++++++++++++++++---------- package.json | 5 +- src/app.js | 6 + src/middleware/auth.js | 43 ++++ src/middleware/jwt-auth.js | 43 ++++ src/middleware/route-ratelimit.js | 21 +- test/v3/integration/rate-limits.js | 194 ++++++++++-------- 7 files changed, 436 insertions(+), 184 deletions(-) create mode 100644 src/middleware/jwt-auth.js diff --git a/package-lock.json b/package-lock.json index a1846d6..794791d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -102,16 +102,18 @@ } }, "@chris.troutner/bch-js": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@chris.troutner/bch-js/-/bch-js-1.4.4.tgz", - "integrity": "sha512-928YAVLKhQ0869/toMkIJl76/7Q31UGVNdYFky1tueJ3BibxgiM+nKl2YbQd06doLuZUniwcD9lKm3sHjFqNbw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@chris.troutner/bch-js/-/bch-js-1.6.1.tgz", + "integrity": "sha512-Wkv7iVw9wYjNF5/Ewy5Qq995x1AV+DWRqikZg9FQxCl7Tub7PmmDfADcJwp+tNKGZs5Q+dpiL30P6zBld52g7g==", "requires": { "apidoc": "^0.17.7", "assert": "^2.0.0", "axios": "^0.19.0", "bc-bip68": "^1.0.5", "bch-wallet-bridge.js": "github:web3bch/bch-wallet-bridge.js#master", + "bchaddrjs-slp": "^0.2.5", "bigi": "^1.4.2", + "bignumber.js": "^9.0.0", "bip-schnorr": "^0.3.0", "bip21": "github:Bitcoin-com/bip21", "bip32-utils": "github:Bitcoin-com/bip32-utils#0.13.1", @@ -141,7 +143,6 @@ "repl.history": "^0.1.4", "safe-buffer": "^5.1.2", "satoshi-bitcoin": "^1.0.4", - "slpjs": "^0.21.0", "socket.io": "^2.1.1", "socket.io-client": "^2.1.1", "touch": "^3.1.0", @@ -287,9 +288,9 @@ "dev": true }, "@semantic-release/github": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-5.4.2.tgz", - "integrity": "sha512-8gkOa5tED/+sjAPwZRYsLaGr6VuAGLZinSvLsuF9/l4qLeYV8gvj7fhjFJepGu6y31t7PR2J9SWzmsqsBAyyKQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-5.5.4.tgz", + "integrity": "sha512-CovZ9zs/rbBOCyyzmstoCwGS5qNEgab3wB8y3SAvOD7TfhtqizWVRkUJV7nzzaevomhUSZD/lp7Cs/fk1sE1Qw==", "dev": true, "requires": { "@octokit/rest": "^16.27.0", @@ -301,16 +302,49 @@ "fs-extra": "^8.0.0", "globby": "^10.0.0", "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "issue-parser": "^4.0.0", + "https-proxy-agent": "^3.0.0", + "issue-parser": "^5.0.0", "lodash": "^4.17.4", "mime": "^2.4.3", "p-filter": "^2.0.0", "p-retry": "^4.0.0", - "parse-github-url": "^1.0.1", "url-join": "^4.0.0" }, "dependencies": { + "https-proxy-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.0.tgz", + "integrity": "sha512-y4jAxNEihqvBI5F3SaO2rtsjIOnnNA8sEbuiP+UhJZJHeM2NRm6c09ax2tgqme+SgUUvjao2fJXF4h3D6Cb2HQ==", + "dev": true, + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "issue-parser": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-5.0.0.tgz", + "integrity": "sha512-q/16W7EPHRL0FKVz9NU++TUsoygXGj6JOi88oulyAcQG+IEZ0T6teVdE+VLbe19OfL/tbV8Wi3Dfo0HedeHW0Q==", + "dev": true, + "requires": { + "lodash.capitalize": "^4.2.1", + "lodash.escaperegexp": "^4.1.2", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.uniqby": "^4.7.0" + } + }, "mime": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", @@ -1466,8 +1500,8 @@ } }, "bchaddrjs-slp": { - "version": "git://github.com/simpleledger/bchaddrjs.git#ea05d679783a6737f2f99adc37ebf485dc64f006", - "from": "git://github.com/simpleledger/bchaddrjs.git#master", + "version": "0.2.6", + "resolved": "git://github.com/simpleledger/bchaddrjs.git#ea05d679783a6737f2f99adc37ebf485dc64f006", "requires": { "bs58check": "^2.1.2", "cashaddrjs-slp": "^0.2.11" @@ -1519,8 +1553,7 @@ "bignumber.js": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", - "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==", - "dev": true + "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" }, "binary-extensions": { "version": "1.13.1", @@ -1711,7 +1744,7 @@ "integrity": "sha512-x0wMVSCZ56ZQnSQ+Xim7XfS0IYiOyOwo0pdMCmR+cDNB7zRhICFuH5i3lNdLSgUw1e7Pc3FmUlC9wruQNlYMnA==" }, "bitcoincashjs-lib": { - "version": "github:christroutner/bitcoincashjs-lib#0a2291b5c5558241003aa9609f82846313604f15", + "version": "github:christroutner/bitcoincashjs-lib#b03ca974d4fd548cb42c7a522863e5d9be6051ef", "from": "github:christroutner/bitcoincashjs-lib", "requires": { "bech32": "^1.1.2", @@ -1884,6 +1917,11 @@ "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", "dev": true }, + "bowser": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.7.0.tgz", + "integrity": "sha512-aIlMvstvu8x+34KEiOHD3AsBgdrzg6sxALYiukOWhFvGMbQI6TRP/iY0LMhUrHs56aD6P1G0Z7h45PUJaa5m9w==" + }, "boxen": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", @@ -2236,6 +2274,11 @@ "resolved": "https://registry.npmjs.org/buffer-compare/-/buffer-compare-1.1.1.tgz", "integrity": "sha1-W+e+hTr4kZjR9N3AkNHWakiu9ZY=" }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + }, "buffer-equals": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/buffer-equals/-/buffer-equals-1.0.4.tgz", @@ -2429,9 +2472,9 @@ "dev": true }, "cashaddrjs": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/cashaddrjs/-/cashaddrjs-0.3.6.tgz", - "integrity": "sha512-TEeTEXT8Gft4cNoZKisCdyrAymnSRYpZHtvCRgXJ9OmOwyTKn5vnCWbPQLL3EiUkm1HeMxSrKNK29nMfvaT2Qw==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cashaddrjs/-/cashaddrjs-0.3.8.tgz", + "integrity": "sha512-gpXj9Qki9noucK0ahPB6dx9A539KQ4TbqKLwl/JsCvD4iLLu1thtoAIDy2hwP6PWg6o1nvK+igsutMA6OvO2Mg==", "requires": { "big-integer": "1.6.36" }, @@ -2722,9 +2765,9 @@ } }, "commander": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.0.tgz", - "integrity": "sha512-pl3QrGOBa9RZaslQiqnnKX2J068wcQw7j9AIaBQ9/JEp5RY6je4jKTImg0Bd+rpoONSe7GUFSgkxLeo17m3Pow==" + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", + "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==" }, "commist": { "version": "1.1.0", @@ -3674,6 +3717,14 @@ "safer-buffer": "^2.1.0" } }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ecurve": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/ecurve/-/ecurve-1.0.6.tgz", @@ -4567,12 +4618,20 @@ } }, "eslint-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.0.tgz", - "integrity": "sha512-7ehnzPaP5IIEh1r1tkjuIrxqhNkzUJa9z3R92tLJdZIVdWaczEhr3EbhGtsMrVxi1KeR8qA7Off6SWc5WNQqyQ==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.0.0" + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + } } }, "eslint-visitor-keys": { @@ -4814,11 +4873,34 @@ "basic-auth": "^2.0.1" } }, + "express-jwt": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-5.3.1.tgz", + "integrity": "sha512-1C9RNq0wMp/JvsH/qZMlg3SIPvKu14YkZ4YYv7gJQ1Vq+Dv8LH9tLKenS5vMNth45gTlEUGx+ycp9IHIlaHP/g==", + "requires": { + "async": "^1.5.0", + "express-unless": "^0.3.0", + "jsonwebtoken": "^8.1.0", + "lodash.set": "^4.0.0" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + } + } + }, "express-rate-limit": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.0.0.tgz", "integrity": "sha512-dhT57wqxfqmkOi4HM7NuT4Gd7gbUgSK2ocG27Y6lwm8lbOAw9XQfeANawGq8wLDtlGPO1ZgDj0HmKsykTxfFAg==" }, + "express-unless": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-0.3.1.tgz", + "integrity": "sha1-JVfBRudb65A+LSR/m1ugFFJpbiA=" + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -6141,9 +6223,9 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" }, "handlebars": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz", - "integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.4.3.tgz", + "integrity": "sha512-B0W4A2U1ww3q7VVthTKfh+epHx+q4mCt6iK+zEAzbMBpWQAwxCeKxEGpj/1oQTpzPXDNSOG7hmG14TsISH50yw==", "requires": { "neo-async": "^2.6.0", "optimist": "^0.6.1", @@ -6323,9 +6405,9 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, "helmet": { - "version": "3.20.0", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.20.0.tgz", - "integrity": "sha512-Ob+TqmQFZ5f7WgP8kBbAzNPsbf6p1lOj5r+327/ymw/IILWih3wcx9u/u/S8Mwv5wbBkO7Li6x5s23t3COhUKw==", + "version": "3.21.1", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-3.21.1.tgz", + "integrity": "sha512-IC/54Lxvvad2YiUdgLmPlNFKLhNuG++waTF5KPYq/Feo3NNhqMFbcLAlbVkai+9q0+4uxjxGPJ9bNykG+3zZNg==", "requires": { "depd": "2.0.0", "dns-prefetch-control": "0.2.0", @@ -6334,14 +6416,14 @@ "feature-policy": "0.3.0", "frameguard": "3.1.0", "helmet-crossdomain": "0.4.0", - "helmet-csp": "2.8.0", + "helmet-csp": "2.9.2", "hide-powered-by": "1.1.0", "hpkp": "2.0.0", "hsts": "2.2.0", "ienoopen": "1.1.0", "nocache": "2.1.0", "referrer-policy": "1.2.0", - "x-xss-protection": "1.2.0" + "x-xss-protection": "1.3.0" }, "dependencies": { "depd": { @@ -6357,14 +6439,14 @@ "integrity": "sha512-AB4DTykRw3HCOxovD1nPR16hllrVImeFp5VBV9/twj66lJ2nU75DP8FPL0/Jp4jj79JhTfG+pFI2MD02kWJ+fA==" }, "helmet-csp": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.8.0.tgz", - "integrity": "sha512-MlCPeM0Sm3pS9RACRihx70VeTHmkQwa7sum9EK1tfw1VZyvFU0dBWym9nHh3CRkTRNlyNm/WFCMvuh9zXkOjNw==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/helmet-csp/-/helmet-csp-2.9.2.tgz", + "integrity": "sha512-Lt5WqNfbNjEJ6ysD4UNpVktSyjEKfU9LVJ1LaFmPfYseg/xPealPfgHhtqdAdjPDopp5zbg/VWCyp4cluMIckw==", "requires": { + "bowser": "^2.6.1", "camelize": "1.0.0", "content-security-policy-builder": "2.1.0", - "dasherize": "2.0.0", - "platform": "1.3.5" + "dasherize": "2.0.0" } }, "help-me": { @@ -6518,27 +6600,6 @@ "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" }, - "https-proxy-agent": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.2.tgz", - "integrity": "sha512-c8Ndjc9Bkpfx/vCJueCPy0jlP4ccCCSNDp8xwCZzPjKJUm+B+u9WX2x98Qx4n1PiMNTWo3D7KK5ifNV/yJyRzg==", - "dev": true, - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, "iconv-lite": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", @@ -7109,19 +7170,6 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", "dev": true }, - "issue-parser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-4.0.0.tgz", - "integrity": "sha512-1RmmAXHl5+cqTZ9dRr861xWy0Gkc9TWTEklgjKv+nhlB1dY1NmGBV8b20jTWRL5cPGpOIXkz84kEcDBM8Nc0cw==", - "dev": true, - "requires": { - "lodash.capitalize": "^4.2.1", - "lodash.escaperegexp": "^4.1.2", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.uniqby": "^4.7.0" - } - }, "istanbul-lib-coverage": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", @@ -7297,6 +7345,30 @@ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" }, + "jsonwebtoken": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", + "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^5.6.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -7324,6 +7396,25 @@ "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==", "dev": true }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "keccak": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/keccak/-/keccak-1.4.0.tgz", @@ -7553,34 +7644,56 @@ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", "dev": true }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" + }, "lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", "dev": true }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" + }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" }, "lodash.isstring": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", - "dev": true + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" }, "lodash.memoize": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=" }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, "lodash.set": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", - "dev": true + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" }, "lodash.toarray": { "version": "4.4.0", @@ -12567,12 +12680,6 @@ "safe-buffer": "^5.1.1" } }, - "parse-github-url": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/parse-github-url/-/parse-github-url-1.0.2.tgz", - "integrity": "sha512-kgBf6avCbO3Cn6+RnzRGLkUsv4ZVqv/VfAYkRsyBcgkshNvVBkRn1FEZcW0Jb+npXQWm2vHPnnOqFteZxRRGNw==", - "dev": true - }, "parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", @@ -12634,6 +12741,14 @@ "passport-strategy": "1.x.x" } }, + "passport-local": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", + "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", + "requires": { + "passport-strategy": "1.x.x" + } + }, "passport-strategy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", @@ -12870,11 +12985,6 @@ "find-up": "^3.0.0" } }, - "platform": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz", - "integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q==" - }, "pluralize": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz", @@ -15254,19 +15364,19 @@ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" }, "uglify-js": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", - "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.3.tgz", + "integrity": "sha512-KfQUgOqTkLp2aZxrMbCuKCDGW9slFYu2A23A36Gs7sGzTLcRBDORdOi5E21KWHFIfkY8kzgi/Pr1cXCh0yIp5g==", "optional": true, "requires": { - "commander": "~2.20.0", + "commander": "~2.20.3", "source-map": "~0.6.1" }, "dependencies": { "commander": { - "version": "2.20.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz", - "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==", + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "optional": true }, "source-map": { @@ -15882,9 +15992,9 @@ } }, "x-xss-protection": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.2.0.tgz", - "integrity": "sha512-xN0kV+8XfOQM2OPPBdEbGtbvJNNP1pvZR7sE6d44cjJFQG4OiGDdienPg5iOUGswBTiGbBvtYDURd30BMJwwqg==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.3.0.tgz", + "integrity": "sha512-kpyBI9TlVipZO4diReZMAHWtS0MMa/7Kgx8hwG/EuZLiA6sg4Ah/4TRdASHhRRN3boobzcYgFRUFSgHRge6Qhg==" }, "xdg-basedir": { "version": "3.0.0", diff --git a/package.json b/package.json index 2c593b5..d184714 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "node": ">=10.15.1" }, "dependencies": { - "@chris.troutner/bch-js": "^1.3.0", + "@chris.troutner/bch-js": "^1.6.1", "apidoc": "^0.17.7", "axios": "^0.19.0", "body-parser": "^1.18.3", @@ -33,7 +33,7 @@ "express": "^4.15.5", "express-basic-auth": "^1.1.3", "express-rate-limit": "^5.0.0", - "helmet": "^3.12.1", + "helmet": "^3.21.1", "level": "^5.0.1", "mkdirp": "^0.5.1", "mocha": "^6.1.4", @@ -42,6 +42,7 @@ "passport": "^0.4.0", "passport-anonymous": "^1.0.1", "passport-http": "^0.3.0", + "passport-local": "^1.0.0", "pg": "^7.11.0", "pg-hstore": "^2.3.2", "slp-sdk": "^4.4.1", diff --git a/src/app.js b/src/app.js index 0b06579..f487d8d 100644 --- a/src/app.js +++ b/src/app.js @@ -15,7 +15,10 @@ const helmet = require("helmet") const debug = require("debug")("rest-cloud:server") const http = require("http") const cors = require("cors") + +// Auth and rate limiting middleware libraries. const AuthMW = require("./middleware/auth") +const jwtAuth = require("./middleware/jwt-auth") // v2 const indexV2 = require("./routes/v2/index") @@ -88,6 +91,9 @@ app.use(express.static(path.join(__dirname, "public"))) const v2prefix = "v2" const v3prefix = "v3" +// Inspect the header for a JWT token. +app.use(`/${v3prefix}/`, jwtAuth.getTokenFromHeaders) + // Instantiate the authorization middleware, used to implement pro-tier rate limiting. const auth = new AuthMW() app.use(`/${v2prefix}/`, auth.mw()) diff --git a/src/middleware/auth.js b/src/middleware/auth.js index f734a4b..86a9e1b 100644 --- a/src/middleware/auth.js +++ b/src/middleware/auth.js @@ -1,6 +1,13 @@ /* Handle authorization for bypassing rate limits. + 1) Default is 'Anonymous Authentication', which unlocks the freemimum tier by + default. + 2) Hard-coded 'Basic Authentication' is a token that does not expire and is + provided to buisiness partners. + 3) JWT-based 'Local Authentication' is used for normal users that pay to + access the premium pro-tier services. + This file uses the passport npm library to check the header of each REST API call for the prescence of a Basic authorization header: https://en.wikipedia.org/wiki/Basic_access_authentication @@ -14,7 +21,9 @@ const passport = require("passport") const BasicStrategy = require("passport-http").BasicStrategy const AnonymousStrategy = require("passport-anonymous") +const LocalStrategy = require("passport-local") const wlogger = require("../util/winston-logging") +const axios = require("axios") // Used for debugging and iterrogating JS objects. const util = require("util") @@ -88,6 +97,40 @@ class AuthMW { return done(null, true) }) ) + + // JWT token based authentication. + passport.use( + new LocalStrategy( + { + usernameField: "user[email]", + passwordField: "user[password]", + passReqToCallback: true, + session: false + }, + async (req, email, password, done) => { + console.log(`Checking against local strategy.`) + + const userData = {} + const isValid = true + + // Lookup the user from the database. + // const userData = await userDB.findByEmail(email) + //console.log(`userData: ${util.inspect(userDataRaw)}`) + + // Hash the password and see if it matches the saved hash. + // const isValid = jwt.validatePassword(userData, password) + + if (isValid) { + //console.log(`Passwords match!`) + return done(null, userData) + } + + return done(null, false, { + errors: { "email or password": "is invalid" } + }) + } + ) + ) } // Middleware called by the route. diff --git a/src/middleware/jwt-auth.js b/src/middleware/jwt-auth.js new file mode 100644 index 0000000..485e7fa --- /dev/null +++ b/src/middleware/jwt-auth.js @@ -0,0 +1,43 @@ +/* + This middleware inspects the request header for a JWT token. + If found, will populate req.locals.jwtToken with the JWT token. +*/ + +"use strict" + +// This function searches the header for the a JWT token in the authorization header. +// If one is found, this middleware passes the JWT token through the +// req.locals.jwtToken property. +const getTokenFromHeaders = (req, res, next) => { + try { + // console.log(`getTokenFromHeaders2: Searching headers for a JWT token.`) + + // console.log(`req.headers: `, req.headers) + + // If the authorization header exists. + if (req.headers.authorization) { + const authStr = req.headers.authorization + + // If the header is proceeded by the word 'Token' + if (authStr.split(" ")[0] === "Token") { + const token = authStr.split(" ")[1] + + // console.log(`JWT found: ${token}`) + + // Create the req.locals property if it does not yet exist. + if (!req.locals) req.locals = {} + req.locals.jwtToken = token + } + } + } catch (err) { + console.log(`Error in getTokenFromHeaders2: `, err) + } + + next() +} + +const jwtAuth = { + getTokenFromHeaders +} + +module.exports = jwtAuth diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index 25aab88..70e2c20 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -24,11 +24,28 @@ const PRO_RPM = 10 * maxRequests const uniqueRateLimits = {} const routeRateLimit = function(req, res, next) { + // Disable rate limiting if 0 passed from RATE_LIMIT_MAX_REQUESTS + if (maxRequests === 0) return next() + // Create a res.locals object if not passed in. if (!req.locals) req.locals = {} - // Disable rate limiting if 0 passed from RATE_LIMIT_MAX_REQUESTS - if (maxRequests === 0) return next() + if (req.locals.jwtToken) + console.log(`req.locals.jwtToken: ${req.locals.jwtToken}`) + + // If no JWT token was provided, skip + if (req.payload) + console.log(`req.payload: ${JSON.stringify(req.payload, null, 2)}`) + // // Unlock the pro-tier rate limits if the user passed in a valid JWT token. + // if (!proRateLimits) { + // const user = await getUserFromJWT(req) + // + // // Enable pro-tier rate limits for this user. + // if(user) { + // console.log(`${user.email} (${user.id}) passed in valid JWT`) + // proRateLimits = true + // } + // } // Current route const rateLimitTier = req.locals.proLimit ? "PRO" : "BASIC" diff --git a/test/v3/integration/rate-limits.js b/test/v3/integration/rate-limits.js index e487d2d..3a71efa 100644 --- a/test/v3/integration/rate-limits.js +++ b/test/v3/integration/rate-limits.js @@ -14,8 +14,11 @@ const axios = require("axios") const util = require("util") util.inspect.defaultOptions = { depth: 1 } -const SERVER = `http://192.168.0.36:12400/v3/` -//const SERVER = `http://localhost:3000/v2/` +// const SERVER = `http://192.168.0.36:12400/v3/` +const SERVER = `http://localhost:3000/v3/` + +const TEST_JWT = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkYTc5ZDk4OTYyMjRjNjM2MmQwYzkwMiIsImlhdCI6MTU3MTUzOTU1MSwiZXhwIjoxNTc0MTMxNTUxfQ.PfPW_Z2NYT1O2zUHXopcz2aLGHSGudaKOIGnt7SuAi4" describe("#rate limits", () => { it("should get control/getNetworkInfo() with no auth", async () => { @@ -32,102 +35,131 @@ describe("#rate limits", () => { assert.hasAnyKeys(result.data, ["version"]) }) - it("should trigger rate-limit handler if rate limits exceeds 30 request per minute", async () => { + // it("should trigger rate-limit handler if rate limits exceeds 30 request per minute", async () => { + // try { + // // Actual rate limit is 60 per minute X 4 nodes = 240 rpm. + // const options = { + // method: "GET", + // url: `${SERVER}control/getNetworkInfo` + // } + // + // const promises = [] + // for (let i = 0; i < 30; i++) { + // const promise = axios(options) + // promises.push(promise) + // } + // + // await Promise.all(promises) + // + // assert.equal(true, false, "Unexpected result!") + // } catch (err) { + // //console.log(`err.response: ${util.inspect(err.response)}`) + // + // assert.equal(err.response.status, 429) + // assert.include(err.response.data.error, "Too many requests") + // } + // }) + + // it("should not trigger rate-limit handler if correct pro-tier password is used", async () => { + // try { + // const username = "BITBOX" + // + // // Pro-tier is accessed by using the right password. + // const password = "BITBOX" + // //const password = "something" + // + // const combined = `${username}:${password}` + // const base64Credential = Buffer.from(combined).toString("base64") + // const readyCredential = `Basic ${base64Credential}` + // + // const options = { + // method: "GET", + // url: `${SERVER}control/getNetworkInfo`, + // headers: { Authorization: readyCredential } + // } + // + // const promises = [] + // for (let i = 0; i < 30; i++) { + // const promise = axios(options) + // promises.push(promise) + // } + // + // await Promise.all(promises) + // + // assert.equal(true, true, "Not throwing an error is a pass!") + // } catch (err) { + // // console.log(`err.response: ${util.inspect(err.response)}`) + // + // assert.equal( + // true, + // false, + // "This error handler should not have been triggered. Is the password correct?" + // ) + // } + // }) + + // it("should trigger rate-limit handler if rate limits exceeds pro-tier limit", async () => { + // try { + // const username = "BITBOX" + // + // // Pro-tier is accessed by using the right password. + // const password = "BITBOX" + // //const password = "something" + // + // const combined = `${username}:${password}` + // const base64Credential = Buffer.from(combined).toString("base64") + // const readyCredential = `Basic ${base64Credential}` + // + // // Actual rate limit is 60 per minute X 4 nodes = 240 rpm. + // const options = { + // method: "GET", + // url: `${SERVER}control/getNetworkInfo`, + // headers: { Authorization: readyCredential } + // } + // + // const promises = [] + // for (let i = 0; i < 80; i++) { + // const promise = axios(options) + // promises.push(promise) + // } + // + // await Promise.all(promises) + // + // assert.equal(true, false, "Unexpected result!") + // } catch (err) { + // //console.log(`err.response: ${util.inspect(err.response)}`) + // + // assert.equal(err.response.status, 429) + // assert.include(err.response.data.error, "Too many requests") + // } + // }) + + it("should unlock pro-tier for a valid JWT token", async () => { try { // Actual rate limit is 60 per minute X 4 nodes = 240 rpm. - const options = { - method: "GET", - url: `${SERVER}control/getNetworkInfo` - } - - const promises = [] - for (let i = 0; i < 30; i++) { - const promise = axios(options) - promises.push(promise) - } - - await Promise.all(promises) - - assert.equal(true, false, "Unexpected result!") - } catch (err) { - //console.log(`err.response: ${util.inspect(err.response)}`) - - assert.equal(err.response.status, 429) - assert.include(err.response.data.error, "Too many requests") - } - }) - - it("should not trigger rate-limit handler if correct pro-tier password is used", async () => { - try { - const username = "BITBOX" - - // Pro-tier is accessed by using the right password. - const password = "BITBOX" - //const password = "something" - - const combined = `${username}:${password}` - const base64Credential = Buffer.from(combined).toString("base64") - const readyCredential = `Basic ${base64Credential}` - const options = { method: "GET", url: `${SERVER}control/getNetworkInfo`, - headers: { Authorization: readyCredential } + headers: { + Authorization: `Token ${TEST_JWT}` + } } const promises = [] - for (let i = 0; i < 30; i++) { + for (let i = 0; i < 1; i++) { const promise = axios(options) promises.push(promise) } await Promise.all(promises) + //assert.equal(true, false, "Unexpected result!") assert.equal(true, true, "Not throwing an error is a pass!") } catch (err) { - // console.log(`err.response: ${util.inspect(err.response)}`) - - assert.equal( - true, - false, - "This error handler should not have been triggered. Is the password correct?" - ) - } - }) - - it("should trigger rate-limit handler if rate limits exceeds pro-tier limit", async () => { - try { - const username = "BITBOX" - - // Pro-tier is accessed by using the right password. - const password = "BITBOX" - //const password = "something" - - const combined = `${username}:${password}` - const base64Credential = Buffer.from(combined).toString("base64") - const readyCredential = `Basic ${base64Credential}` - - // Actual rate limit is 60 per minute X 4 nodes = 240 rpm. - const options = { - method: "GET", - url: `${SERVER}control/getNetworkInfo`, - headers: { Authorization: readyCredential } - } - - const promises = [] - for (let i = 0; i < 80; i++) { - const promise = axios(options) - promises.push(promise) - } - - await Promise.all(promises) + console.log(`err.response: ${util.inspect(err.response)}`) assert.equal(true, false, "Unexpected result!") - } catch (err) { - //console.log(`err.response: ${util.inspect(err.response)}`) - - assert.equal(err.response.status, 429) - assert.include(err.response.data.error, "Too many requests") } - }) + // Override default timeout for this test. + }).timeout(20000) }) From b9b5c52c40809cf8246de2e5c4382d584e3ce7b7 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 20 Oct 2019 11:59:25 -0700 Subject: [PATCH 02/49] Removed local auth lib and code. Not needed for JWT --- package-lock.json | 137 +++++++---------------------------------- package.json | 1 - src/middleware/auth.js | 35 ----------- 3 files changed, 22 insertions(+), 151 deletions(-) diff --git a/package-lock.json b/package-lock.json index 794791d..6af3271 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2274,11 +2274,6 @@ "resolved": "https://registry.npmjs.org/buffer-compare/-/buffer-compare-1.1.1.tgz", "integrity": "sha1-W+e+hTr4kZjR9N3AkNHWakiu9ZY=" }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" - }, "buffer-equals": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/buffer-equals/-/buffer-equals-1.0.4.tgz", @@ -3717,14 +3712,6 @@ "safer-buffer": "^2.1.0" } }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, "ecurve": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/ecurve/-/ecurve-1.0.6.tgz", @@ -4873,34 +4860,11 @@ "basic-auth": "^2.0.1" } }, - "express-jwt": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/express-jwt/-/express-jwt-5.3.1.tgz", - "integrity": "sha512-1C9RNq0wMp/JvsH/qZMlg3SIPvKu14YkZ4YYv7gJQ1Vq+Dv8LH9tLKenS5vMNth45gTlEUGx+ycp9IHIlaHP/g==", - "requires": { - "async": "^1.5.0", - "express-unless": "^0.3.0", - "jsonwebtoken": "^8.1.0", - "lodash.set": "^4.0.0" - }, - "dependencies": { - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" - } - } - }, "express-rate-limit": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.0.0.tgz", "integrity": "sha512-dhT57wqxfqmkOi4HM7NuT4Gd7gbUgSK2ocG27Y6lwm8lbOAw9XQfeANawGq8wLDtlGPO1ZgDj0HmKsykTxfFAg==" }, - "express-unless": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-0.3.1.tgz", - "integrity": "sha1-JVfBRudb65A+LSR/m1ugFFJpbiA=" - }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -7345,30 +7309,6 @@ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" }, - "jsonwebtoken": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", - "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "requires": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -7396,25 +7336,6 @@ "integrity": "sha512-FrLwOgm+iXrPV+5zDU6Jqu4gCRXbWEQg2O3SKONsWE4w7AXFRkryS53bpWdaL9cNol+AmR3AEYz6kn+o0fCPnw==", "dev": true }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, "keccak": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/keccak/-/keccak-1.4.0.tgz", @@ -7644,56 +7565,34 @@ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", "dev": true }, - "lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8=" - }, - "lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY=" - }, - "lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha1-YZwK89A/iwTDH1iChAt3sRzWg0M=" - }, "lodash.ismatch": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", "integrity": "sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc=", "dev": true }, - "lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w=" - }, "lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", + "dev": true }, "lodash.isstring": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" + "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=", + "dev": true }, "lodash.memoize": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-3.0.4.tgz", "integrity": "sha1-LcvSwofLwKVcxCMovQxzYVDVPj8=" }, - "lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" - }, "lodash.set": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=" + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", + "dev": true }, "lodash.toarray": { "version": "4.4.0", @@ -12741,14 +12640,6 @@ "passport-strategy": "1.x.x" } }, - "passport-local": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz", - "integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=", - "requires": { - "passport-strategy": "1.x.x" - } - }, "passport-strategy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", @@ -14138,6 +14029,14 @@ "touch": "^3.1.0" }, "dependencies": { + "bchaddrjs-slp": { + "version": "git://github.com/simpleledger/bchaddrjs.git#ea05d679783a6737f2f99adc37ebf485dc64f006", + "from": "git://github.com/simpleledger/bchaddrjs.git#master", + "requires": { + "bs58check": "^2.1.2", + "cashaddrjs-slp": "^0.2.11" + } + }, "bignumber.js": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-8.1.1.tgz", @@ -14196,6 +14095,14 @@ "is-buffer": "^2.0.2" } }, + "bchaddrjs-slp": { + "version": "git://github.com/simpleledger/bchaddrjs.git#ea05d679783a6737f2f99adc37ebf485dc64f006", + "from": "git://github.com/simpleledger/bchaddrjs.git#master", + "requires": { + "bs58check": "^2.1.2", + "cashaddrjs-slp": "^0.2.11" + } + }, "bignumber.js": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-8.1.1.tgz", diff --git a/package.json b/package.json index d184714..29938dd 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,6 @@ "passport": "^0.4.0", "passport-anonymous": "^1.0.1", "passport-http": "^0.3.0", - "passport-local": "^1.0.0", "pg": "^7.11.0", "pg-hstore": "^2.3.2", "slp-sdk": "^4.4.1", diff --git a/src/middleware/auth.js b/src/middleware/auth.js index 86a9e1b..97ae4df 100644 --- a/src/middleware/auth.js +++ b/src/middleware/auth.js @@ -21,7 +21,6 @@ const passport = require("passport") const BasicStrategy = require("passport-http").BasicStrategy const AnonymousStrategy = require("passport-anonymous") -const LocalStrategy = require("passport-local") const wlogger = require("../util/winston-logging") const axios = require("axios") @@ -97,40 +96,6 @@ class AuthMW { return done(null, true) }) ) - - // JWT token based authentication. - passport.use( - new LocalStrategy( - { - usernameField: "user[email]", - passwordField: "user[password]", - passReqToCallback: true, - session: false - }, - async (req, email, password, done) => { - console.log(`Checking against local strategy.`) - - const userData = {} - const isValid = true - - // Lookup the user from the database. - // const userData = await userDB.findByEmail(email) - //console.log(`userData: ${util.inspect(userDataRaw)}`) - - // Hash the password and see if it matches the saved hash. - // const isValid = jwt.validatePassword(userData, password) - - if (isValid) { - //console.log(`Passwords match!`) - return done(null, userData) - } - - return done(null, false, { - errors: { "email or password": "is invalid" } - }) - } - ) - ) } // Middleware called by the route. From ccb0b3bfab0904f81228152116f5cb461bc7aa60 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 20 Oct 2019 12:33:16 -0700 Subject: [PATCH 03/49] Integrated remote JWT auth server --- src/middleware/auth.js | 1 - src/middleware/route-ratelimit.js | 44 ++++--- test/v3/integration/rate-limits.js | 198 ++++++++++++++--------------- 3 files changed, 126 insertions(+), 117 deletions(-) diff --git a/src/middleware/auth.js b/src/middleware/auth.js index 97ae4df..682c23d 100644 --- a/src/middleware/auth.js +++ b/src/middleware/auth.js @@ -22,7 +22,6 @@ const passport = require("passport") const BasicStrategy = require("passport-http").BasicStrategy const AnonymousStrategy = require("passport-anonymous") const wlogger = require("../util/winston-logging") -const axios = require("axios") // Used for debugging and iterrogating JS objects. const util = require("util") diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index 70e2c20..a2d56ee 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -11,11 +11,12 @@ const express = require("express") const RateLimit = require("express-rate-limit") +const axios = require("axios") // Set max requests per minute const maxRequests = process.env.RATE_LIMIT_MAX_REQUESTS ? parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) - : 30 + : 10 // Pro-tier rate limits are 10x the freemium limits. const PRO_RPM = 10 * maxRequests @@ -23,29 +24,38 @@ const PRO_RPM = 10 * maxRequests // Unique route mapped to its rate limit const uniqueRateLimits = {} -const routeRateLimit = function(req, res, next) { +const routeRateLimit = async function(req, res, next) { // Disable rate limiting if 0 passed from RATE_LIMIT_MAX_REQUESTS if (maxRequests === 0) return next() // Create a res.locals object if not passed in. if (!req.locals) req.locals = {} - if (req.locals.jwtToken) - console.log(`req.locals.jwtToken: ${req.locals.jwtToken}`) + // Warn if JWT_AUTH_SERVER env var is not set. + const authServer = process.env.JWT_AUTH_SERVER + if (!authServer || authServer === "") { + console.warn( + "JWT_AUTH_SERVER env var is not set. JWT tokens not being evaluated." + ) + } else { + // If a JWT token is passed in, validate it and enable pro-tier rate limits + // if it's valid. + if (req.locals.jwtToken) { + // console.log(`req.locals.jwtToken: ${req.locals.jwtToken}`) - // If no JWT token was provided, skip - if (req.payload) - console.log(`req.payload: ${JSON.stringify(req.payload, null, 2)}`) - // // Unlock the pro-tier rate limits if the user passed in a valid JWT token. - // if (!proRateLimits) { - // const user = await getUserFromJWT(req) - // - // // Enable pro-tier rate limits for this user. - // if(user) { - // console.log(`${user.email} (${user.id}) passed in valid JWT`) - // proRateLimits = true - // } - // } + const path = `${authServer}apitoken/isvalid/${req.locals.jwtToken}` + + let isValidJwt = await axios.get(path) + isValidJwt = isValidJwt.data + // console.log(`isValidJwt: ${JSON.stringify(isValidJwt, null, 2)}`) + + // Enable pro-tier rate limits if JWT if valid. + if (isValidJwt) { + // console.log(`JWT is valid. Enabling pro-tier rate limits.`) + req.locals.proLimit = true + } + } + } // Current route const rateLimitTier = req.locals.proLimit ? "PRO" : "BASIC" diff --git a/test/v3/integration/rate-limits.js b/test/v3/integration/rate-limits.js index 3a71efa..99ba139 100644 --- a/test/v3/integration/rate-limits.js +++ b/test/v3/integration/rate-limits.js @@ -29,124 +29,124 @@ describe("#rate limits", () => { const result = await axios(options) //console.log(`result.status: ${result.status}`) - //console.log(`result.data: ${util.inspect(result.data)}`) + // console.log(`result.data: ${util.inspect(result.data)}`) assert.equal(result.status, 200) assert.hasAnyKeys(result.data, ["version"]) }) - // it("should trigger rate-limit handler if rate limits exceeds 30 request per minute", async () => { - // try { - // // Actual rate limit is 60 per minute X 4 nodes = 240 rpm. - // const options = { - // method: "GET", - // url: `${SERVER}control/getNetworkInfo` - // } - // - // const promises = [] - // for (let i = 0; i < 30; i++) { - // const promise = axios(options) - // promises.push(promise) - // } - // - // await Promise.all(promises) - // - // assert.equal(true, false, "Unexpected result!") - // } catch (err) { - // //console.log(`err.response: ${util.inspect(err.response)}`) - // - // assert.equal(err.response.status, 429) - // assert.include(err.response.data.error, "Too many requests") - // } - // }) + it("should trigger rate-limit handler if rate limits exceeds 30 request per minute", async () => { + try { + // Actual rate limit is 60 per minute X 4 nodes = 240 rpm. + const options = { + method: "GET", + url: `${SERVER}control/getNetworkInfo` + } - // it("should not trigger rate-limit handler if correct pro-tier password is used", async () => { - // try { - // const username = "BITBOX" - // - // // Pro-tier is accessed by using the right password. - // const password = "BITBOX" - // //const password = "something" - // - // const combined = `${username}:${password}` - // const base64Credential = Buffer.from(combined).toString("base64") - // const readyCredential = `Basic ${base64Credential}` - // - // const options = { - // method: "GET", - // url: `${SERVER}control/getNetworkInfo`, - // headers: { Authorization: readyCredential } - // } - // - // const promises = [] - // for (let i = 0; i < 30; i++) { - // const promise = axios(options) - // promises.push(promise) - // } - // - // await Promise.all(promises) - // - // assert.equal(true, true, "Not throwing an error is a pass!") - // } catch (err) { - // // console.log(`err.response: ${util.inspect(err.response)}`) - // - // assert.equal( - // true, - // false, - // "This error handler should not have been triggered. Is the password correct?" - // ) - // } - // }) + const promises = [] + for (let i = 0; i < 30; i++) { + const promise = axios(options) + promises.push(promise) + } - // it("should trigger rate-limit handler if rate limits exceeds pro-tier limit", async () => { - // try { - // const username = "BITBOX" - // - // // Pro-tier is accessed by using the right password. - // const password = "BITBOX" - // //const password = "something" - // - // const combined = `${username}:${password}` - // const base64Credential = Buffer.from(combined).toString("base64") - // const readyCredential = `Basic ${base64Credential}` - // - // // Actual rate limit is 60 per minute X 4 nodes = 240 rpm. - // const options = { - // method: "GET", - // url: `${SERVER}control/getNetworkInfo`, - // headers: { Authorization: readyCredential } - // } - // - // const promises = [] - // for (let i = 0; i < 80; i++) { - // const promise = axios(options) - // promises.push(promise) - // } - // - // await Promise.all(promises) - // - // assert.equal(true, false, "Unexpected result!") - // } catch (err) { - // //console.log(`err.response: ${util.inspect(err.response)}`) - // - // assert.equal(err.response.status, 429) - // assert.include(err.response.data.error, "Too many requests") - // } - // }) + await Promise.all(promises) + + assert.equal(true, false, "Unexpected result!") + } catch (err) { + //console.log(`err.response: ${util.inspect(err.response)}`) + + assert.equal(err.response.status, 429) + assert.include(err.response.data.error, "Too many requests") + } + }) + + it("should not trigger rate-limit handler if correct pro-tier password is used", async () => { + try { + const username = "BITBOX" + + // Pro-tier is accessed by using the right password. + const password = "BITBOX" + //const password = "something" + + const combined = `${username}:${password}` + const base64Credential = Buffer.from(combined).toString("base64") + const readyCredential = `Basic ${base64Credential}` + + const options = { + method: "GET", + url: `${SERVER}control/getNetworkInfo`, + headers: { Authorization: readyCredential } + } + + const promises = [] + for (let i = 0; i < 30; i++) { + const promise = axios(options) + promises.push(promise) + } + + await Promise.all(promises) + + assert.equal(true, true, "Not throwing an error is a pass!") + } catch (err) { + // console.log(`err.response: ${util.inspect(err.response)}`) + + assert.equal( + true, + false, + "This error handler should not have been triggered. Is the password correct?" + ) + } + }) + + it("should trigger rate-limit handler if rate limits exceeds pro-tier limit", async () => { + try { + const username = "BITBOX" + + // Pro-tier is accessed by using the right password. + const password = "BITBOX" + //const password = "something" + + const combined = `${username}:${password}` + const base64Credential = Buffer.from(combined).toString("base64") + const readyCredential = `Basic ${base64Credential}` + + // Actual rate limit is 60 per minute X 4 nodes = 240 rpm. + const options = { + method: "GET", + url: `${SERVER}control/getNetworkInfo`, + headers: { Authorization: readyCredential } + } + + const promises = [] + for (let i = 0; i < 80; i++) { + const promise = axios(options) + promises.push(promise) + } + + await Promise.all(promises) + + assert.equal(true, false, "Unexpected result!") + } catch (err) { + //console.log(`err.response: ${util.inspect(err.response)}`) + + assert.equal(err.response.status, 429) + assert.include(err.response.data.error, "Too many requests") + } + }) it("should unlock pro-tier for a valid JWT token", async () => { try { // Actual rate limit is 60 per minute X 4 nodes = 240 rpm. const options = { method: "GET", - url: `${SERVER}control/getNetworkInfo`, + url: `${SERVER}control/`, headers: { Authorization: `Token ${TEST_JWT}` } } const promises = [] - for (let i = 0; i < 1; i++) { + for (let i = 0; i < 60; i++) { const promise = axios(options) promises.push(promise) } From 41d048f6a5f7f099c1d0481bbd048db68abeb0a5 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Thu, 31 Oct 2019 13:21:59 -0700 Subject: [PATCH 04/49] fix(apiLevel): passing apiLevel is req.locals --- src/middleware/auth.js | 20 ++++------- src/middleware/jwt-auth.js | 9 +++-- src/middleware/route-ratelimit.js | 18 +++++++--- src/routes/v3/control.js | 55 ++----------------------------- 4 files changed, 29 insertions(+), 73 deletions(-) diff --git a/src/middleware/auth.js b/src/middleware/auth.js index 682c23d..307a583 100644 --- a/src/middleware/auth.js +++ b/src/middleware/auth.js @@ -42,18 +42,6 @@ class AuthMW { _this = this // Initialize passport for 'anonymous' authentication. - /* - passport.use( - new AnonymousStrategy({ passReqToCallback: true }, function( - req, - username, - password, - done - ) { - console.log(`anonymous auth handler triggered.`) - }) - ) - */ passport.use(new AnonymousStrategy()) // Initialize passport for 'basic' authentication. @@ -69,7 +57,13 @@ class AuthMW { //console.log(`password: ${password}`) // Create the req.locals property if it does not yet exist. - if (!req.locals) req.locals = {} + if (!req.locals) { + req.locals = { + // default values + proLimit: false, + apiLevel: 0 + } + } // Set pro-tier rate limit to flag to false by default. req.locals.proLimit = false diff --git a/src/middleware/jwt-auth.js b/src/middleware/jwt-auth.js index 485e7fa..b006035 100644 --- a/src/middleware/jwt-auth.js +++ b/src/middleware/jwt-auth.js @@ -25,8 +25,13 @@ const getTokenFromHeaders = (req, res, next) => { // console.log(`JWT found: ${token}`) // Create the req.locals property if it does not yet exist. - if (!req.locals) req.locals = {} - req.locals.jwtToken = token + if (!req.locals) { + req.locals = { + jwtToken: token, + proLimit: false, + apiLevel: 0 + } + } } } } catch (err) { diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index a2d56ee..5bc6886 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -29,7 +29,14 @@ const routeRateLimit = async function(req, res, next) { if (maxRequests === 0) return next() // Create a res.locals object if not passed in. - if (!req.locals) req.locals = {} + if (!req.locals) { + req.locals = { + // default values + jwtToken: "", + proLimit: false, + apiLevel: 0 + } + } // Warn if JWT_AUTH_SERVER env var is not set. const authServer = process.env.JWT_AUTH_SERVER @@ -45,14 +52,15 @@ const routeRateLimit = async function(req, res, next) { const path = `${authServer}apitoken/isvalid/${req.locals.jwtToken}` - let isValidJwt = await axios.get(path) - isValidJwt = isValidJwt.data - // console.log(`isValidJwt: ${JSON.stringify(isValidJwt, null, 2)}`) + let jwtInfo = await axios.get(path) + jwtInfo = jwtInfo.data + // console.log(`jwtInfo: ${JSON.stringify(jwtInfo, null, 2)}`) // Enable pro-tier rate limits if JWT if valid. - if (isValidJwt) { + if (jwtInfo.isValid) { // console.log(`JWT is valid. Enabling pro-tier rate limits.`) req.locals.proLimit = true + req.locals.apiLevel = jwtInfo.apiLevel } } } diff --git a/src/routes/v3/control.js b/src/routes/v3/control.js index da6eb78..6a3f6ee 100644 --- a/src/routes/v3/control.js +++ b/src/routes/v3/control.js @@ -12,64 +12,12 @@ const util = require("util") util.inspect.defaultOptions = { depth: 1 } router.get("/", root) -router.get("/getinfo", getInfo) router.get("/getnetworkinfo", getNetworkInfo) function root(req, res, next) { return res.json({ status: "control" }) } -/** - * @api {get} /control/getinfo Get full node info - * @apiName GetInfo - * @apiGroup Control - * @apiDescription RPC call which gets basic full node information. - * - * @apiExample Example usage: - * curl -X GET "https://mainnet.bchjs.cash/v3/control/getinfo" -H "accept: application/json" - * - * @apiSuccess {Object} object Object containing data - * @apiSuccess {Number} object.version Full node version - * @apiSuccess {Number} object.protocolversion Protocol version - * @apiSuccess {Number} object.blocks Current block - * @apiSuccess {Number} object.timeoffset ? - * @apiSuccess {Number} object.connections Number of connected peers - * @apiSuccess {String} object.proxy ? - * @apiSuccess {Number} object.difficulty Current difficulty setting - * @apiSuccess {Boolean} object.testnet testnet = true, mainnet = false - * @apiSuccess {Number} object.paytxfee ? - * @apiSuccess {Number} object.relayfee ? - * @apiSuccess {String} object.errors Recent errors - */ -async function getInfo(req, res, next) { - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "getinfo" - requestConfig.data.method = "getinfo" - requestConfig.data.params = [] - - try { - const response = await BitboxHTTP(requestConfig) - - return res.json(response.data.result) - } catch (error) { - wlogger.error(`Error in control.ts/getInfo().`, error) - - // Write out error to error log. - //logger.error(`Error in control/getInfo: `, error) - - res.status(500) - if (error.response && error.response.data && error.response.data.error) - return res.json({ error: error.response.data.error }) - return res.json({ error: util.inspect(error) }) - } -} - /** * @api {get} /control/getnetworkinfo Get Network Info * @apiName GetNetworkInfo @@ -88,6 +36,8 @@ async function getNetworkInfo(req, res, next) { requestConfig } = routeUtils.setEnvVars() + console.log(`req.locals: ${JSON.stringify(req.locals, null, 2)}`) + requestConfig.data.id = "getnetworkinfo" requestConfig.data.method = "getnetworkinfo" requestConfig.data.params = [] @@ -150,7 +100,6 @@ module.exports = { router, testableComponents: { root, - getInfo, getNetworkInfo } } From 17525a40d5a4bede156cb85c15e6f44973d1c7fa Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Thu, 31 Oct 2019 14:15:53 -0700 Subject: [PATCH 05/49] feat(rate-limit tiers): Created finer-resolution rate limits + unit tests --- src/middleware/route-ratelimit.js | 18 +++++--- test/v3/rate-limits.js | 73 +++++++++++++++++++++++++------ 2 files changed, 72 insertions(+), 19 deletions(-) diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index 5bc6886..2bb2c2b 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -1,10 +1,13 @@ /* This file controls the request-per-minute (RPM) rate limits. - It is assumed that this middleware is run AFTER the auth.js middleware which - checks for Basic auth. If the user adds the correct Basic auth to the header - of their API request, they will get pro-tier rate limits. By default, the - freemium rate limits apply. + It is assumed that this middleware is run AFTER the jwt-auth.js and auth.js + middleware. + + Current rate limiting rules in requests-per-minute: + - anonymous access: 3 + - free access: 10 + - any paid tier: 100 */ "use strict" @@ -16,10 +19,10 @@ const axios = require("axios") // Set max requests per minute const maxRequests = process.env.RATE_LIMIT_MAX_REQUESTS ? parseInt(process.env.RATE_LIMIT_MAX_REQUESTS) - : 10 + : 3 // Pro-tier rate limits are 10x the freemium limits. -const PRO_RPM = 10 * maxRequests +// const PRO_RPM = 10 * maxRequests // Unique route mapped to its rate limit const uniqueRateLimits = {} @@ -87,6 +90,9 @@ const routeRateLimit = async function(req, res, next) { // TODO: replace the console.logs with calls to our logging system. //console.log(`applying pro-rate limits`) + let PRO_RPM = 10 // Default value for free tier + if (req.locals.apiLevel > 0) PRO_RPM = 100 // RPM for paid tiers. + // Create new RateLimit if none exists for this route if (!uniqueRateLimits[route]) { uniqueRateLimits[route] = new RateLimit({ diff --git a/test/v3/rate-limits.js b/test/v3/rate-limits.js index 4459e4a..3b2db1f 100644 --- a/test/v3/rate-limits.js +++ b/test/v3/rate-limits.js @@ -62,12 +62,67 @@ describe("#route-ratelimits", () => { assert.equal(next.called, true) }) - it("should trigger rate-limit handler if rate limits exceeds 25 request per minute", async () => { + it("should trigger rate-limit handler if rate limits exceeds 5 request per minute", async () => { req.baseUrl = "/v3" req.path = "/control/getNetworkInfo" req.method = "GET" - for (let i = 0; i < 35; i++) { + for (let i = 0; i < 5; i++) { + next.reset() // reset the stubbed next() function. + + await routeRateLimit(req, res, next) + //console.log(`next() called: ${next.called}`) + } + + // Note: next() will be called unless the rate-limit kicks in. + assert.equal( + next.called, + false, + `next should not be called if rate limit was triggered.` + ) + }) + + it("should NOT trigger rate-limit for free-tier at 5 RPM", async () => { + // Clear the require cache before running this test. + delete require.cache[ + require.resolve("../../src/middleware/route-ratelimit") + ] + rateLimitMiddleware = require("../../src/middleware/route-ratelimit") + routeRateLimit = rateLimitMiddleware.routeRateLimit + + req.baseUrl = "/v3" + req.path = "/control/getNetworkInfo" + req.method = "GET" + + req.locals.proLimit = true + req.locals.apiLevel = 0 + + for (let i = 0; i < 5; i++) { + next.reset() // reset the stubbed next() function. + + await routeRateLimit(req, res, next) + //console.log(`next() called: ${next.called}`) + } + + //console.log(`req.locals after test: ${util.inspect(req.locals)}`) + + // Note: next() will be called unless the rate-limit kicks in. + assert.equal( + next.called, + true, + `next should be called if rate limit was not triggered.` + ) + }) + + it("should trigger rate-limit for free tier 10 RPM", async () => { + req.baseUrl = "/v3" + req.path = "/control/getNetworkInfo" + req.method = "GET" + + req.locals.proLimit = true + req.locals.apiLevel = 0 + + for (let i = 0; i < 12; i++) { next.reset() // reset the stubbed next() function. await routeRateLimit(req, res, next) @@ -95,11 +150,7 @@ describe("#route-ratelimits", () => { req.method = "GET" req.locals.proLimit = true - - //console.log(`req.locals before test: ${util.inspect(req.locals)}`) - - // Prepare the authorization header - //req.headers.authorization = generateAuthHeader("BITBOX") + req.locals.apiLevel = 10 for (let i = 0; i < 25; i++) { next.reset() // reset the stubbed next() function. @@ -131,13 +182,9 @@ describe("#route-ratelimits", () => { req.method = "GET" req.locals.proLimit = true + req.locals.apiLevel = 10 - //console.log(`req.locals before test: ${util.inspect(req.locals)}`) - - // Prepare the authorization header - //req.headers.authorization = generateAuthHeader("BITBOX") - - for (let i = 0; i < 400; i++) { + for (let i = 0; i < 150; i++) { next.reset() // reset the stubbed next() function. await routeRateLimit(req, res, next) From f74b33cf9b3d0cb68635c659a8792fdb95a0e9ce Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Thu, 31 Oct 2019 14:19:22 -0700 Subject: [PATCH 06/49] feat(no v2): Removing v2 routes --- src/app.js | 45 +- src/routes/v2/address.js | 805 -------------- src/routes/v2/block.js | 280 ----- src/routes/v2/blockchain.js | 890 --------------- src/routes/v2/control.js | 95 -- src/routes/v2/generating.js | 51 - src/routes/v2/health-check.js | 11 - src/routes/v2/index.js | 15 - src/routes/v2/logging.js | 51 - src/routes/v2/mining.js | 170 --- src/routes/v2/network.js | 168 --- src/routes/v2/rawtransactions.js | 609 ----------- src/routes/v2/route-utils.js | 139 --- src/routes/v2/services/bitbox.js | 11 - src/routes/v2/services/requestUtils.js | 20 - src/routes/v2/slp.js | 1369 ------------------------ src/routes/v2/transaction.js | 188 ---- src/routes/v2/util.js | 173 --- 18 files changed, 2 insertions(+), 5088 deletions(-) delete mode 100644 src/routes/v2/address.js delete mode 100644 src/routes/v2/block.js delete mode 100644 src/routes/v2/blockchain.js delete mode 100644 src/routes/v2/control.js delete mode 100644 src/routes/v2/generating.js delete mode 100644 src/routes/v2/health-check.js delete mode 100644 src/routes/v2/index.js delete mode 100644 src/routes/v2/logging.js delete mode 100644 src/routes/v2/mining.js delete mode 100644 src/routes/v2/network.js delete mode 100644 src/routes/v2/rawtransactions.js delete mode 100644 src/routes/v2/route-utils.js delete mode 100644 src/routes/v2/services/bitbox.js delete mode 100644 src/routes/v2/services/requestUtils.js delete mode 100644 src/routes/v2/slp.js delete mode 100644 src/routes/v2/transaction.js delete mode 100644 src/routes/v2/util.js diff --git a/src/app.js b/src/app.js index f487d8d..f9114df 100644 --- a/src/app.js +++ b/src/app.js @@ -20,21 +20,6 @@ const cors = require("cors") const AuthMW = require("./middleware/auth") const jwtAuth = require("./middleware/jwt-auth") -// v2 -const indexV2 = require("./routes/v2/index") -const healthCheckV2 = require("./routes/v2/health-check") -const addressV2 = require("./routes/v2/address") -const blockV2 = require("./routes/v2/block") -const blockchainV2 = require("./routes/v2/blockchain") -const controlV2 = require("./routes/v2/control") -const generatingV2 = require("./routes/v2/generating") -const miningV2 = require("./routes/v2/mining") -const networkV2 = require("./routes/v2/network") -const rawtransactionsV2 = require("./routes/v2/rawtransactions") -const transactionV2 = require("./routes/v2/transaction") -const utilV2 = require("./routes/v2/util") -const slpV2 = require("./routes/v2/slp") - // v3 const healthCheckV3 = require("./routes/v3/health-check") const blockchainV3 = require("./routes/v3/blockchain") @@ -78,17 +63,7 @@ app.use(bodyParser.urlencoded({ extended: false })) app.use(cookieParser()) app.use(express.static(path.join(__dirname, "public"))) -// -// let username = process.env.USERNAME; -// let password = process.env.PASSWORD; -// -// app.use(basicAuth( -// { -// users: { username: password } -// } -// )); - -const v2prefix = "v2" +// const v2prefix = "v2" const v3prefix = "v3" // Inspect the header for a JWT token. @@ -96,25 +71,9 @@ app.use(`/${v3prefix}/`, jwtAuth.getTokenFromHeaders) // Instantiate the authorization middleware, used to implement pro-tier rate limiting. const auth = new AuthMW() -app.use(`/${v2prefix}/`, auth.mw()) +// app.use(`/${v2prefix}/`, auth.mw()) app.use(`/${v3prefix}/`, auth.mw()) -// Rate limit on all v2 routes -app.use(`/${v2prefix}/`, routeRateLimit) -app.use("/", indexV2) -app.use(`/${v2prefix}/` + `health-check`, healthCheckV2) -app.use(`/${v2prefix}/` + `address`, addressV2.router) -app.use(`/${v2prefix}/` + `blockchain`, blockchainV2.router) -app.use(`/${v2prefix}/` + `block`, blockV2.router) -app.use(`/${v2prefix}/` + `control`, controlV2.router) -app.use(`/${v2prefix}/` + `generating`, generatingV2) -app.use(`/${v2prefix}/` + `mining`, miningV2.router) -app.use(`/${v2prefix}/` + `network`, networkV2) -app.use(`/${v2prefix}/` + `rawtransactions`, rawtransactionsV2.router) -app.use(`/${v2prefix}/` + `transaction`, transactionV2.router) -app.use(`/${v2prefix}/` + `util`, utilV2.router) -app.use(`/${v2prefix}/` + `slp`, slpV2.router) - // Rate limit on all v3 routes app.use(`/${v3prefix}/`, routeRateLimit) app.use(`/${v3prefix}/` + `health-check`, healthCheckV3) diff --git a/src/routes/v2/address.js b/src/routes/v2/address.js deleted file mode 100644 index 0ae8d9e..0000000 --- a/src/routes/v2/address.js +++ /dev/null @@ -1,805 +0,0 @@ -/* - Address route -*/ - -"use strict" - -const express = require("express") -const requestUtils = require("./services/requestUtils") -const axios = require("axios") -const logger = require("./logging.js") -const routeUtils = require("./route-utils") -const wlogger = require("../../util/winston-logging") - -//const router = express.Router() -const router = express.Router() - -// Used for processing error messages before sending them to the user. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -const BITBOXJS = require("@chris.troutner/bch-js") -const BITBOX = new BITBOXJS() - -// Use the default (and max) page size of 1000 -// https://github.com/bitpay/insight-api#notes-on-upgrading-from-v03 -const PAGE_SIZE = 1000 - -// Connect the route endpoints to their handler functions. -router.get("/", root) -router.get("/details/:address", detailsSingle) -router.post("/details", detailsBulk) -router.post("/utxo", utxoBulk) -router.get("/utxo/:address", utxoSingle) -router.post("/unconfirmed", unconfirmedBulk) -router.get("/unconfirmed/:address", unconfirmedSingle) -router.get("/transactions/:address", transactionsSingle) -router.post("/transactions", transactionsBulk) -router.get("/fromXPub/:xpub", fromXPubSingle) - -// Root API endpoint. Simply acknowledges that it exists. -function root(req, res, next) { - return res.json({ status: "address" }) -} - -// Query the Insight API for details on a single BCH address. -// Returns a Promise. -async function detailsFromInsight(thisAddress, currentPage = 0) { - try { - let addr - if ( - process.env.BITCOINCOM_BASEURL === "https://bch-insight.bitpay.com/api/" - ) - addr = BITBOX.Address.toCashAddress(thisAddress) - else addr = BITBOX.Address.toLegacyAddress(thisAddress) - - let path = `${process.env.BITCOINCOM_BASEURL}addr/${addr}` - - // Set from and to params based on currentPage and pageSize - // https://github.com/bitpay/insight-api/blob/master/README.md#notes-on-upgrading-from-v02 - const from = currentPage * PAGE_SIZE - const to = from + PAGE_SIZE - path = `${path}?from=${from}&to=${to}` - - // Query the Insight server. - const axiosResponse = await axios.get(path) - const retData = axiosResponse.data - //console.log(`retData: ${util.inspect(retData)}`) - - // Calculate pagesTotal from response - const pagesTotal = Math.ceil(retData.txApperances / PAGE_SIZE) - - // Append different address formats to the return data. - retData.legacyAddress = BITBOX.Address.toLegacyAddress(retData.addrStr) - retData.cashAddress = BITBOX.Address.toCashAddress(retData.addrStr) - delete retData.addrStr - - // Append pagination information to the return data. - retData.currentPage = currentPage - retData.pagesTotal = pagesTotal - - return retData - } catch (err) { - // Dev Note: Do not log error messages here. Throw them instead and let the - // parent function handle it. - throw err - } -} - -// POST handler for bulk queries on address details -// curl -d '{"addresses": ["bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", "bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"]}' -H "Content-Type: application/json" http://localhost:3000/v2/address/details -// curl -d '{"addresses": ["bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", "bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"], "from": 1, "to": 5}' -H "Content-Type: application/json" http://localhost:3000/v2/address/details -async function detailsBulk(req, res, next) { - try { - let addresses = req.body.addresses - const currentPage = req.body.page ? parseInt(req.body.page, 10) : 0 - - // Reject if addresses is not an array. - if (!Array.isArray(addresses)) { - res.status(400) - return res.json({ - error: "addresses needs to be an array. Use GET for single address." - }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, addresses)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - logger.debug(`Executing address/details with these addresses: `, addresses) - wlogger.debug(`Executing address/details with these addresses: `, addresses) - - // Validate each element in the address array. - for (let i = 0; i < addresses.length; i++) { - const thisAddress = addresses[i] - - // Ensure the input is a valid BCH address. - try { - BITBOX.Address.toLegacyAddress(thisAddress) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${thisAddress}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(thisAddress) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network for address ${thisAddress}. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - } - - // Loops through each address and creates an array of Promises, querying - // Insight API in parallel. - addresses = addresses.map(async (address, index) => - detailsFromInsight(address, currentPage) - ) - - // Wait for all parallel Insight requests to return. - const result = await axios.all(addresses) - - // Return the array of retrieved address information. - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - //logger.error(`Error in detailsBulk(): `, err) - wlogger.error(`Error in address.ts/detailsBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// GET handler for single address details -async function detailsSingle(req, res, next) { - try { - const address = req.params.address - const currentPage = req.query.page ? parseInt(req.query.page, 10) : 0 - - if (!address || address === "") { - res.status(400) - return res.json({ error: "address can not be empty" }) - } - - // Reject if address is an array. - if (Array.isArray(address)) { - res.status(400) - return res.json({ - error: "address can not be an array. Use POST for bulk upload." - }) - } - - logger.debug(`Executing address/detailsSingle with this address: `, address) - wlogger.debug( - `Executing address/detailsSingle with this address: `, - address - ) - - // Ensure the input is a valid BCH address. - try { - var legacyAddr = BITBOX.Address.toLegacyAddress(address) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${address}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(address) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - - // Query the Insight API. - const retData = await detailsFromInsight(address, currentPage) - - // Return the retrieved address information. - res.status(200) - return res.json(retData) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in address.ts/detailsSingle: `, err) - wlogger.error(`Error in address.ts/detailsSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Retrieve UTXO data from the Insight API -async function utxoFromInsight(thisAddress) { - try { - let addr - if ( - process.env.BITCOINCOM_BASEURL === "https://bch-insight.bitpay.com/api/" - ) - addr = BITBOX.Address.toCashAddress(thisAddress) - else addr = BITBOX.Address.toLegacyAddress(thisAddress) - - const path = `${process.env.BITCOINCOM_BASEURL}addr/${addr}/utxo` - - // Query the Insight server. - const response = await axios.get(path) - - // Append different address formats to the return data. - const retData = { - utxos: Array, - legacyAddress: String, - cashAddress: String, - scriptPubKey: String - } - if (response.data.length && response.data[0].scriptPubKey) { - const spk = response.data[0].scriptPubKey - retData.scriptPubKey = spk - } - retData.legacyAddress = BITBOX.Address.toLegacyAddress(thisAddress) - retData.cashAddress = BITBOX.Address.toCashAddress(thisAddress) - retData.utxos = response.data.map(utxo => { - delete utxo.address - delete utxo.scriptPubKey - return utxo - }) - //console.log(`utxoFromInsight retData: ${util.inspect(retData)}`) - - return retData - } catch (err) { - // Dev Note: Do not log error messages here. Throw them instead and let the - // parent function handle it. - throw err - } -} - -// Retrieve UTXO information for an address. -async function utxoBulk(req, res, next) { - try { - let addresses = req.body.addresses - - // Reject if address is not an array. - if (!Array.isArray(addresses)) { - res.status(400) - return res.json({ error: "addresses needs to be an array" }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, addresses)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - // Validate each element in the address array. - for (let i = 0; i < addresses.length; i++) { - const thisAddress = addresses[i] - - // Ensure the input is a valid BCH address. - try { - BITBOX.Address.toLegacyAddress(thisAddress) - } catch (er) { - //if (er.message.includes("Unsupported address format")) - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${thisAddress}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(thisAddress) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network for address ${thisAddress}. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - } - - logger.debug(`Executing address/utxoBulk with these addresses: `, addresses) - wlogger.debug( - `Executing address/utxoBulk with these addresses: `, - addresses - ) - - // Loops through each address and creates an array of Promises, querying - // Insight API in parallel. - addresses = addresses.map(async (address, index) => - utxoFromInsight(address) - ) - - // Wait for all parallel Insight requests to return. - const result = await axios.all(addresses) - - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in address.ts/utxoBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// GET handler for single address details -async function utxoSingle(req, res, next) { - try { - const address = req.params.address - if (!address || address === "") { - res.status(400) - return res.json({ error: "address can not be empty" }) - } - - // Reject if address is an array. - if (Array.isArray(address)) { - res.status(400) - return res.json({ - error: "address can not be an array. Use POST for bulk upload." - }) - } - - logger.debug(`Executing address/utxoSingle with this address: `, address) - wlogger.debug(`Executing address/utxoSingle with this address: `, address) - - // Ensure the input is a valid BCH address. - try { - var legacyAddr = BITBOX.Address.toLegacyAddress(address) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${address}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(address) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - - // Query the Insight API. - const retData = await utxoFromInsight(address) - - // Return the array of retrieved address information. - res.status(200) - return res.json(retData) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in address.ts/utxoSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Retrieve any unconfirmed TX information for a given address. -async function unconfirmedBulk(req, res, next) { - try { - const addresses = req.body.addresses - - // Reject if address is not an array. - if (!Array.isArray(addresses)) { - res.status(400) - return res.json({ error: "addresses needs to be an array" }) - } - - logger.debug(`Executing address/utxo with these addresses: `, addresses) - wlogger.debug(`Executing address/utxo with these addresses: `, addresses) - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, addresses)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - // Validate each element in the address array. - for (let i = 0; i < addresses.length; i++) { - const thisAddress = addresses[i] - - // Ensure the input is a valid BCH address. - try { - BITBOX.Address.toLegacyAddress(thisAddress) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${thisAddress}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(thisAddress) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network for address ${thisAddress}. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - } - - // Collect an array of promises. - const promises = addresses.map(address => utxoFromInsight(address)) - - // Wait for all parallel Insight requests to return. - const result = await axios.all(promises) - - // Loop through each result - const finalResult = result.map(elem => { - //console.log(`elem: ${util.inspect(elem)}`) - - // Filter out confirmed transactions. - const unconfirmedUtxos = elem.utxos.filter( - utxo => utxo.confirmations === 0 - ) - - elem.utxos = unconfirmedUtxos - - return elem - }) - - // Return the array of retrieved address information. - res.status(200) - return res.json(finalResult) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in address.ts/unconfirmedBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// GET handler. Retrieve any unconfirmed TX information for a given address. -async function unconfirmedSingle(req, res, next) { - try { - const address = req.params.address - if (!address || address === "") { - res.status(400) - return res.json({ error: "address can not be empty" }) - } - - // Reject if address is an array. - if (Array.isArray(address)) { - res.status(400) - return res.json({ - error: "address can not be an array. Use POST for bulk upload." - }) - } - - logger.debug(`Executing address/utxoSingle with this address: `, address) - wlogger.debug(`Executing address/utxoSingle with this address: `, address) - - // Ensure the input is a valid BCH address. - try { - var legacyAddr = BITBOX.Address.toLegacyAddress(address) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${address}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(address) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - - // Query the Insight API. - const retData = await utxoFromInsight(address) - //console.log(`retData: ${JSON.stringify(retData,null,2)}`) - - // Loop through each returned UTXO. - const unconfirmedUTXOs = [] - for (let j = 0; j < retData.utxos.length; j++) { - const thisUtxo = retData.utxos[j] - - // Only interested in UTXOs with no confirmations. - if (thisUtxo.confirmations === 0) unconfirmedUTXOs.push(thisUtxo) - } - - retData.utxos = unconfirmedUTXOs - - // Return the array of retrieved address information. - res.status(200) - return res.json(retData) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in address.ts/unconfirmedSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Retrieve transaction data from the Insight API -async function transactionsFromInsight(thisAddress, currentPage = 0) { - try { - const path = `${process.env.BITCOINCOM_BASEURL}txs/?address=${thisAddress}&pageNum=${currentPage}` - - // Query the Insight server. - const response = await axios.get(path) - - // Append different address formats to the return data. - const retData = response.data - retData.legacyAddress = BITBOX.Address.toLegacyAddress(thisAddress) - retData.cashAddress = BITBOX.Address.toCashAddress(thisAddress) - retData.currentPage = currentPage - - return retData - } catch (err) { - // Dev Note: Do not log error messages here. Throw them instead and let the - // parent function handle it. - throw err - } -} - -// Get an array of TX information for a given address. -async function transactionsBulk(req, res, next) { - try { - let addresses = req.body.addresses - const currentPage = req.body.page ? parseInt(req.body.page, 10) : 0 - - // Reject if address is not an array. - if (!Array.isArray(addresses)) { - res.status(400) - return res.json({ error: "addresses needs to be an array" }) - } - - logger.debug(`Executing address/utxo with these addresses: `, addresses) - wlogger.debug(`Executing address/utxo with these addresses: `, addresses) - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, addresses)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - // Validate each element in the address array. - for (let i = 0; i < addresses.length; i++) { - const thisAddress = addresses[i] - - // Ensure the input is a valid BCH address. - try { - BITBOX.Address.toLegacyAddress(thisAddress) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${thisAddress}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(thisAddress) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network for address ${thisAddress}. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - } - - // Loop through each address and collect an array of promises. - addresses = addresses.map(async (address, index) => - transactionsFromInsight(address, currentPage) - ) - - // Wait for all parallel Insight requests to return. - const result = await axios.all(addresses) - - // Return the array of retrieved address information. - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in address.ts/transactionsBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// GET handler. Retrieve any unconfirmed TX information for a given address. -async function transactionsSingle(req, res, next) { - try { - const address = req.params.address - const currentPage = req.query.page ? parseInt(req.query.page, 10) : 0 - - if (!address || address === "") { - res.status(400) - return res.json({ error: "address can not be empty" }) - } - - // Reject if address is an array. - if (Array.isArray(address)) { - res.status(400) - return res.json({ - error: "address can not be an array. Use POST for bulk upload." - }) - } - - logger.debug( - `Executing address/transactionsSingle with this address: `, - address - ) - wlogger.debug( - `Executing address/transactionsSingle with this address: `, - address - ) - - // Ensure the input is a valid BCH address. - try { - var legacyAddr = BITBOX.Address.toLegacyAddress(address) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${address}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(address) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - - // Query the Insight API. - const retData = await transactionsFromInsight(address, currentPage) - //console.log(`retData: ${JSON.stringify(retData,null,2)}`) - - // Return the array of retrieved address information. - res.status(200) - return res.json(retData) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in address.ts/transactionsSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -async function fromXPubSingle(req, res, next) { - try { - const xpub = req.params.xpub - const hdPath = req.query.hdPath ? req.query.hdPath : "0" - - if (!xpub || xpub === "") { - res.status(400) - return res.json({ error: "xpub can not be empty" }) - } - - // Reject if xpub is an array. - if (Array.isArray(xpub)) { - res.status(400) - return res.json({ - error: "xpub can not be an array. Use POST for bulk upload." - }) - } - - logger.debug(`Executing address/fromXPub with this xpub: `, xpub) - wlogger.debug(`Executing address/fromXPub with this xpub: `, xpub) - - const cashAddr = BITBOX.Address.fromXPub(xpub, hdPath) - const legacyAddr = BITBOX.Address.toLegacyAddress(cashAddr) - res.status(200) - return res.json({ - cashAddress: cashAddr, - legacyAddress: legacyAddr - }) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in address.ts/fromXPubSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -module.exports = { - router, - testableComponents: { - root, - detailsBulk, - detailsSingle, - utxoBulk, - utxoSingle, - unconfirmedBulk, - unconfirmedSingle, - transactionsBulk, - transactionsSingle, - fromXPubSingle - } -} diff --git a/src/routes/v2/block.js b/src/routes/v2/block.js deleted file mode 100644 index 500687f..0000000 --- a/src/routes/v2/block.js +++ /dev/null @@ -1,280 +0,0 @@ -"use strict" - -const express = require("express") -const requestUtils = require("./services/requestUtils") -const axios = require("axios") -const bitbox = require("./services/bitbox") -const logger = require("./logging.js") -const wlogger = require("../../util/winston-logging") - -const routeUtils = require("./route-utils") - -// Used for processing error messages before sending them to the user. -const util = require("util") -util.inspect.defaultOptions = { depth: 3 } - -const router = express.Router() -//const BitboxHTTP = bitbox.getInstance() - -router.get("/", root) -router.get("/detailsByHash/:hash", detailsByHashSingle) -router.post("/detailsByHash", detailsByHashBulk) -router.get("/detailsByHeight/:height", detailsByHeightSingle) -router.post("/detailsByHeight", detailsByHeightBulk) - -function root(req, res, next) { - return res.json({ status: "block" }) -} - -// Call the insight server to get block details based on the hash. -async function detailsByHashSingle(req, res, next) { - try { - const hash = req.params.hash - - // Reject if hash is empty - if (!hash || hash === "") { - res.status(400) - return res.json({ error: "hash must not be empty" }) - } - - const response = await axios.get( - `${process.env.BITCOINCOM_BASEURL}block/${hash}` - ) - //console.log(`response.data: ${JSON.stringify(response.data,null,2)}`) - - const parsed = response.data - return res.json(parsed) - } catch (error) { - //console.log(`error object: ${util.inspect(error)}`) - - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(error) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - if (error.response && error.response.status === 404) { - res.status(404) - return res.json({ error: "Not Found" }) - } - - // Write out error to error log. - //logger.error(`Error in block/detailsByHash: `, error) - wlogger.error(`Error in block.ts/detailsByHashSingle().`, error) - - res.status(500) - return res.json({ error: util.inspect(error) }) - } -} - -async function detailsByHashBulk(req, res, next) { - try { - const hashes = req.body.hashes - - // Reject if hashes is not an array. - if (!Array.isArray(hashes)) { - res.status(400) - return res.json({ - error: "hashes needs to be an array. Use GET for single address." - }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, hashes)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - // Validate each hash in the array. - for (let i = 0; i < hashes.length; i++) { - const thisHash = hashes[i] - - if (thisHash.length !== 64) { - res.status(400) - return res.json({ - error: `Invalid hash. Double check your hash is valid: ${thisHash}` - }) - } - } - - // Loop through each hash and creates an array of promises - const axiosPromises = hashes.map(async hash => - axios.get(`${process.env.BITCOINCOM_BASEURL}block/${hash}`) - ) - - // Wait for all parallel promises to return. - const axiosResult = await axios.all(axiosPromises) - - // Extract the data component from the axios response. - const result = axiosResult.map(x => x.data) - //console.log(`result: ${util.inspect(result)}`) - - res.status(200) - return res.json(result) - } catch (error) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(error) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - if (error.response && error.response.status === 404) { - res.status(404) - return res.json({ error: "Not Found" }) - } - - // Write out error to error log. - //logger.error(`Error in block/detailsByHash: `, error) - wlogger.error(`Error in block.ts/detailsByHashBulk().`, error) - - res.status(500) - return res.json({ error: util.inspect(error) }) - } -} - -// Call the Full Node to get block hash based on height, then call the Insight -// server to get details from that hash. -async function detailsByHeightSingle(req, res, next) { - try { - const height = req.params.height - - // Reject if id is empty - if (!height || height === "") { - res.status(400) - return res.json({ error: "height must not be empty" }) - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "getblockhash" - requestConfig.data.method = "getblockhash" - requestConfig.data.params = [parseInt(height)] - - const response = await BitboxHTTP(requestConfig) - - const hash = response.data.result - //console.log(`response.data: ${util.inspect(response.data)}`) - - // Call detailsByHashSingle now that the hash has been retrieved. - req.params.hash = hash - return detailsByHashSingle(req, res, next) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in control/getInfo: `, error) - wlogger.error(`Error in block.ts/detailsByHeightSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -async function detailsByHeightBulk(req, res, next) { - try { - const heights = req.body.heights - - // Reject if heights is not an array. - if (!Array.isArray(heights)) { - res.status(400) - return res.json({ - error: "heights needs to be an array. Use GET for single height." - }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, heights)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - logger.debug(`Executing detailsByHeight with these heights: `, heights) - - // Validate each element in the address array. - for (let i = 0; i < heights.length; i++) { - const thisHeight = heights[i] - - // Reject if id is empty - if (!thisHeight || thisHeight === "") { - res.status(400) - return res.json({ error: "height must not be empty" }) - } - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - // Loop through each height and creates an array of requests to call in parallel - const promises = heights.map(async height => { - requestConfig.data.id = "getblockhash" - requestConfig.data.method = "getblockhash" - requestConfig.data.params = [parseInt(height)] - - const response = await BitboxHTTP(requestConfig) - - const hash = response.data.result - - const axiosResult = await axios.get( - `${process.env.BITCOINCOM_BASEURL}block/${hash}` - ) - - return axiosResult.data - }) - - // Wait for all parallel Insight requests to return. - const result = await axios.all(promises) - - res.status(200) - return res.json(result) - } catch (error) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(error) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - if (error.response && error.response.status === 404) { - res.status(404) - return res.json({ error: "Not Found" }) - } - - // Write out error to error log. - //logger.error(`Error in block/detailsByHash: `, error) - wlogger.error(`Error in block.ts/detailsByHeightBulk().`, error) - - res.status(500) - return res.json({ error: util.inspect(error) }) - } -} - -module.exports = { - router, - testableComponents: { - root, - detailsByHashSingle, - detailsByHashBulk, - detailsByHeightSingle, - detailsByHeightBulk - } -} diff --git a/src/routes/v2/blockchain.js b/src/routes/v2/blockchain.js deleted file mode 100644 index 65d772d..0000000 --- a/src/routes/v2/blockchain.js +++ /dev/null @@ -1,890 +0,0 @@ -/* - TODO - - Add blockhash functionality back into getTxOutProof -*/ - -"use strict" - -const express = require("express") -const router = express.Router() -const axios = require("axios") - -const routeUtils = require("./route-utils") -const logger = require("./logging.js") -const wlogger = require("../../util/winston-logging") - -// Used to convert error messages to strings, to safely pass to users. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -// Define routes. -router.get("/", root) -router.get("/getBestBlockHash", getBestBlockHash) -// Dev Note: getBlock/:hash ommited because its the same as block/detailsByHash -//router.get("/getBlock/:hash", getBlock) -router.get("/getBlockchainInfo", getBlockchainInfo) -router.get("/getBlockCount", getBlockCount) -router.get("/getBlockHeader/:hash", getBlockHeaderSingle) -router.post("/getBlockHeader", getBlockHeaderBulk) - -router.get("/getChainTips", getChainTips) -router.get("/getDifficulty", getDifficulty) -router.get("/getMempoolEntry/:txid", getMempoolEntrySingle) -router.post("/getMempoolEntry", getMempoolEntryBulk) -router.get("/getMempoolInfo", getMempoolInfo) -router.get("/getRawMempool", getRawMempool) -router.get("/getTxOut/:txid/:n", getTxOut) -router.get("/getTxOutProof/:txid", getTxOutProofSingle) -router.post("/getTxOutProof", getTxOutProofBulk) -router.get("/verifyTxOutProof/:proof", verifyTxOutProofSingle) -router.post("/verifyTxOutProof", verifyTxOutProofBulk) - -function root(req, res, next) { - return res.json({ status: "blockchain" }) -} - -// Returns the hash of the best (tip) block in the longest block chain. -async function getBestBlockHash(req, res, next) { - try { - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "getbestblockhash" - requestConfig.data.method = "getbestblockhash" - requestConfig.data.params = [] - - const response = await BitboxHTTP(requestConfig) - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in blockchain.ts/getBestBlockHash().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -async function getBlockchainInfo(req, res, next) { - try { - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "getblockchaininfo" - requestConfig.data.method = "getblockchaininfo" - requestConfig.data.params = [] - - const response = await BitboxHTTP(requestConfig) - - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in blockchain.ts/getBlockchainInfo().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -async function getBlockCount(req, res, next) { - try { - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "getblockcount" - requestConfig.data.method = "getblockcount" - requestConfig.data.params = [] - - const response = await BitboxHTTP(requestConfig) - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in blockchain.ts/getBlockCount().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -async function getBlockHeaderSingle(req, res, next) { - try { - let verbose = false - if (req.query.verbose && req.query.verbose.toString() === "true") - verbose = true - - const hash = req.params.hash - if (!hash || hash === "") { - res.status(400) - return res.json({ error: "hash can not be empty" }) - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "getblockheader" - requestConfig.data.method = "getblockheader" - requestConfig.data.params = [hash, verbose] - - const response = await BitboxHTTP(requestConfig) - - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in blockchain.ts/getBlockHeaderSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -async function getBlockHeaderBulk(req, res, next) { - try { - const hashes = req.body.hashes - const verbose = req.body.verbose ? req.body.verbose : false - - if (!Array.isArray(hashes)) { - res.status(400) - return res.json({ - error: "hashes needs to be an array. Use GET for single hash." - }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, hashes)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - logger.debug( - `Executing blockchain/getBlockHeaderBulk with these hashes: `, - hashes - ) - - // Validate each hash in the array. - for (let i = 0; i < hashes.length; i++) { - const hash = hashes[i] - - if (hash.length !== 64) { - res.status(400) - return res.json({ error: `This is not a hash: ${hash}` }) - } - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - // Loop through each hash and creates an array of requests to call in parallel - const promises = hashes.map(async hash => { - requestConfig.data.id = "getblockheader" - requestConfig.data.method = "getblockheader" - requestConfig.data.params = [hash, verbose] - - return await BitboxHTTP(requestConfig) - }) - - const axiosResult = await axios.all(promises) - - // Extract the data component from the axios response. - const result = axiosResult.map(x => x.data.result) - - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in blockchain.ts/getBlockHeaderBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -async function getChainTips(req, res, next) { - try { - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "getchaintips" - requestConfig.data.method = "getchaintips" - requestConfig.data.params = [] - - const response = await BitboxHTTP(requestConfig) - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in blockchain.ts/getChainTips().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Get the current difficulty value, used to regulate mining power on the network. -async function getDifficulty(req, res, next) { - try { - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "getdifficulty" - requestConfig.data.method = "getdifficulty" - requestConfig.data.params = [] - - const response = await BitboxHTTP(requestConfig) - - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in blockchain.ts/getDifficulty().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Returns mempool data for given transaction. TXID must be in mempool (unconfirmed) -async function getMempoolEntrySingle(req, res, next) { - try { - // Validate input parameter - const txid = req.params.txid - if (!txid || txid === "") { - res.status(400) - return res.json({ error: "txid can not be empty" }) - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "getmempoolentry" - requestConfig.data.method = "getmempoolentry" - requestConfig.data.params = [txid] - - const response = await BitboxHTTP(requestConfig) - - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in blockchain.ts/getMempoolEntrySingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -async function getMempoolEntryBulk(req, res, next) { - try { - const txids = req.body.txids - - if (!Array.isArray(txids)) { - res.status(400) - return res.json({ - error: "txids needs to be an array. Use GET for single txid." - }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, txids)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - logger.debug( - `Executing blockchain/getMempoolEntry with these txids: `, - txids - ) - - // Validate each element in the array - for (let i = 0; i < txids.length; i++) { - const txid = txids[i] - - if (txid.length !== 64) { - res.status(400) - return res.json({ error: "This is not a txid" }) - } - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - // Loop through each txid and creates an array of requests to call in parallel - const promises = txids.map(async txid => { - requestConfig.data.id = "getmempoolentry" - requestConfig.data.method = "getmempoolentry" - requestConfig.data.params = [txid] - - return await BitboxHTTP(requestConfig) - }) - - const axiosResult = await axios.all(promises) - - // Extract the data component from the axios response. - const result = axiosResult.map(x => x.data.result) - - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in blockchain.ts/getMempoolEntryBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -async function getMempoolInfo(req, res, next) { - try { - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "getmempoolinfo" - requestConfig.data.method = "getmempoolinfo" - requestConfig.data.params = [] - - const response = await BitboxHTTP(requestConfig) - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in blockchain.ts/getMempoolInfo().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -async function getRawMempool(req, res, next) { - try { - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - let verbose = false - if (req.query.verbose && req.query.verbose === "true") verbose = true - - requestConfig.data.id = "getrawmempool" - requestConfig.data.method = "getrawmempool" - requestConfig.data.params = [verbose] - - const response = await BitboxHTTP(requestConfig) - - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in blockchain.ts/getRawMempool().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Returns details about an unspent transaction output. -async function getTxOut(req, res, next) { - try { - // Validate input parameter - const txid = req.params.txid - if (!txid || txid === "") { - res.status(400) - return res.json({ error: "txid can not be empty" }) - } - - let n = req.params.n - if (n === undefined || n === "") { - res.status(400) - return res.json({ error: "n can not be empty" }) - } - n = parseInt(n) - - let include_mempool = false - if (req.query.include_mempool && req.query.include_mempool === "true") - include_mempool = true - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "gettxout" - requestConfig.data.method = "gettxout" - requestConfig.data.params = [txid, n, include_mempool] - - const response = await BitboxHTTP(requestConfig) - - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in blockchain.ts/getTxOut().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Returns a hex-encoded proof that 'txid' was included in a block. -async function getTxOutProofSingle(req, res, next) { - try { - // Validate input parameter - const txid = req.params.txid - if (!txid || txid === "") { - res.status(400) - return res.json({ error: "txid can not be empty" }) - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "gettxoutproof" - requestConfig.data.method = "gettxoutproof" - requestConfig.data.params = [[txid]] - - const response = await BitboxHTTP(requestConfig) - - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in blockchain.ts/getTxOutProofSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Returns a hex-encoded proof that 'txid' was included in a block. -async function getTxOutProofBulk(req, res, next) { - try { - const txids = req.body.txids - - // Reject if txids is not an array. - if (!Array.isArray(txids)) { - res.status(400) - return res.json({ - error: "txids needs to be an array. Use GET for single txid." - }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, txids)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - // Validate each element in the array. - for (let i = 0; i < txids.length; i++) { - const txid = txids[i] - - if (txid.length !== 64) { - res.status(400) - return res.json({ - error: `Invalid txid. Double check your txid is valid: ${txid}` - }) - } - } - - logger.debug(`Executing blockchain/getTxOutProof with these txids: `, txids) - - // Loop through each txid and creates an array of requests to call in parallel - const promises = txids.map(async txid => { - requestConfig.data.id = "gettxoutproof" - requestConfig.data.method = "gettxoutproof" - requestConfig.data.params = [[txid]] - - return await BitboxHTTP(requestConfig) - }) - - // Wait for all parallel promisses to resolve. - const axiosResult = await axios.all(promises) - - // Extract the data component from the axios response. - const result = axiosResult.map(x => x.data.result) - - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in blockchain.ts/getTxOutProofBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -/* -// -// router.get('/preciousBlock/:hash', async (req, res, next) => { -// BitboxHTTP({ -// method: 'post', -// auth: { -// username: username, -// password: password -// }, -// data: { -// jsonrpc: "1.0", -// id:"preciousblock", -// method: "preciousblock", -// params: [ -// req.params.hash -// ] -// } -// }) -// .then((response) => { -// res.json(JSON.stringify(response.data.result)); -// }) -// .catch((error) => { -// res.send(error.response.data.error.message); -// }); -// }); -// -// router.post('/pruneBlockchain/:height', async (req, res, next) => { -// BitboxHTTP({ -// method: 'post', -// auth: { -// username: username, -// password: password -// }, -// data: { -// jsonrpc: "1.0", -// id:"pruneblockchain", -// method: "pruneblockchain", -// params: [ -// req.params.height -// ] -// } -// }) -// .then((response) => { -// res.json(response.data.result); -// }) -// .catch((error) => { -// res.send(error.response.data.error.message); -// }); -// }); -// -// router.get('/verifyChain', async (req, res, next) => { -// BitboxHTTP({ -// method: 'post', -// auth: { -// username: username, -// password: password -// }, -// data: { -// jsonrpc: "1.0", -// id:"verifychain", -// method: "verifychain" -// } -// }) -// .then((response) => { -// res.json(response.data.result); -// }) -// .catch((error) => { -// res.send(error.response.data.error.message); -// }); -// }); -*/ - -async function verifyTxOutProofSingle(req, res, next) { - try { - // Validate input parameter - const proof = req.params.proof - if (!proof || proof === "") { - res.status(400) - return res.json({ error: "proof can not be empty" }) - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "verifytxoutproof" - requestConfig.data.method = "verifytxoutproof" - requestConfig.data.params = [req.params.proof] - - const response = await BitboxHTTP(requestConfig) - - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in blockchain.ts/verifyTxOutProofSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -async function verifyTxOutProofBulk(req, res, next) { - try { - const proofs = req.body.proofs - - // Reject if proofs is not an array. - if (!Array.isArray(proofs)) { - res.status(400) - return res.json({ - error: "proofs needs to be an array. Use GET for single proof." - }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, proofs)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - // Validate each element in the array. - for (let i = 0; i < proofs.length; i++) { - const proof = proofs[i] - - if (!proof || proof === "") { - res.status(400) - return res.json({ error: `proof can not be empty: ${proof}` }) - } - } - - logger.debug( - `Executing blockchain/verifyTxOutProof with these proofs: `, - proofs - ) - - // Loop through each proof and creates an array of requests to call in parallel - const promises = proofs.map(async proof => { - requestConfig.data.id = "verifytxoutproof" - requestConfig.data.method = "verifytxoutproof" - requestConfig.data.params = [proof] - - return await BitboxHTTP(requestConfig) - }) - - // Wait for all parallel promisses to resolve. - const axiosResult = await axios.all(promises) - - // Extract the data component from the axios response. - const result = axiosResult.map(x => x.data.result[0]) - - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in blockchain.ts/verifyTxOutProofBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -module.exports = { - router, - testableComponents: { - root, - getBestBlockHash, - //getBlock, - getBlockchainInfo, - getBlockCount, - getBlockHeaderSingle, - getBlockHeaderBulk, - getChainTips, - getDifficulty, - getMempoolInfo, - getRawMempool, - getMempoolEntrySingle, - getMempoolEntryBulk, - getTxOut, - getTxOutProofSingle, - getTxOutProofBulk, - verifyTxOutProofSingle, - verifyTxOutProofBulk - } -} diff --git a/src/routes/v2/control.js b/src/routes/v2/control.js deleted file mode 100644 index fabc233..0000000 --- a/src/routes/v2/control.js +++ /dev/null @@ -1,95 +0,0 @@ -"use strict" - -const express = require("express") -const router = express.Router() -const axios = require("axios") - -const logger = require("./logging.js") -const routeUtils = require("./route-utils") -const wlogger = require("../../util/winston-logging") - -// Used for processing error messages before sending them to the user. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -router.get("/", root) -router.get("/getInfo", getInfo) - -function root(req, res, next) { - return res.json({ status: "control" }) -} - -// Execute the RPC getinfo call. -async function getInfo(req, res, next) { - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "getinfo" - requestConfig.data.method = "getinfo" - requestConfig.data.params = [] - - try { - const response = await BitboxHTTP(requestConfig) - - return res.json(response.data.result) - } catch (error) { - wlogger.error(`Error in control.ts/getInfo().`, error) - - // Write out error to error log. - //logger.error(`Error in control/getInfo: `, error) - - res.status(500) - if (error.response && error.response.data && error.response.data.error) - return res.json({ error: error.response.data.error }) - return res.json({ error: util.inspect(error) }) - } -} - -// router.get('/getMemoryInfo', (req, res, next) => { -// BitboxHTTP({ -// method: 'post', -// auth: { -// username: username, -// password: password -// }, -// data: { -// jsonrpc: "1.0", -// id:"getmemoryinfo", -// method: "getmemoryinfo" -// } -// }) -// .then((response) => { -// res.json(response.data.result); -// }) -// .catch((error) => { -// res.send(error.response.data.error.message); -// }); -// }); -// -// router.get('/help', (req, res, next) => { -// BITBOX.Control.help() -// .then((result) => { -// res.json(result); -// }, (err) => { console.log(err); -// }); -// }); -// -// router.post('/stop', (req, res, next) => { -// BITBOX.Control.stop() -// .then((result) => { -// res.json(result); -// }, (err) => { console.log(err); -// }); -// }); - -module.exports = { - router, - testableComponents: { - root, - getInfo - } -} diff --git a/src/routes/v2/generating.js b/src/routes/v2/generating.js deleted file mode 100644 index ec0c601..0000000 --- a/src/routes/v2/generating.js +++ /dev/null @@ -1,51 +0,0 @@ -"use strict" - -const express = require("express") -const router = express.Router() -//const axios = require("axios"); - -//const BITBOXCli = require("bitbox-sdk/lib/bitbox-sdk").default; -//const BITBOX = new BITBOXCli(); - -//const BitboxHTTP = axios.create({ -// baseURL: process.env.RPC_BASEURL, -//}); -//const username = process.env.RPC_USERNAME; -//const password = process.env.RPC_PASSWORD; - -router.get("/", (req, res, next) => { - res.json({ status: "generating" }) -}) -// -// router.post('/generateToAddress/:nblocks/:address', (req, res, next) => { -// let maxtries = 1000000; -// if(req.query.maxtries) { -// maxtries = parseInt(req.query.maxtries); -// } -// -// BitboxHTTP({ -// method: 'post', -// auth: { -// username: username, -// password: password -// }, -// data: { -// jsonrpc: "1.0", -// id:"generatetoaddress", -// method: "generatetoaddress", -// params: [ -// req.params.nblocks, -// req.params.address, -// maxtries -// ] -// } -// }) -// .then((response) => { -// res.json(response.data.result); -// }) -// .catch((error) => { -// res.send(error.response.data.error.message); -// }); -// }); - -module.exports = router diff --git a/src/routes/v2/health-check.js b/src/routes/v2/health-check.js deleted file mode 100644 index 8c05030..0000000 --- a/src/routes/v2/health-check.js +++ /dev/null @@ -1,11 +0,0 @@ -"use strict" - -const express = require("express") -const router = express.Router() - -/* GET home page. */ -router.get("/", (req, res, next) => { - res.json({ status: "winning v2" }) -}) - -module.exports = router diff --git a/src/routes/v2/index.js b/src/routes/v2/index.js deleted file mode 100644 index 33c175f..0000000 --- a/src/routes/v2/index.js +++ /dev/null @@ -1,15 +0,0 @@ -"use strict" - -const express = require("express") -const router = express.Router() - -/* GET home page. */ -router.get("/", (req, res, next) => { - res.render("swagger-v2") -}) - -router.get("/v2", (req, res, next) => { - res.render("swagger-v2") -}) - -module.exports = router diff --git a/src/routes/v2/logging.js b/src/routes/v2/logging.js deleted file mode 100644 index 07fd9be..0000000 --- a/src/routes/v2/logging.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - A utility library for setting up greylog2 logging. -*/ - -"use strict" - -// This will be uncommented and correct once we have our logging server functioning. -/* -var graylog2 = require("graylog2"); -var logger = new graylog2.graylog({ - servers: [ - { 'host': '127.0.0.1', port: 12201 }, - { 'host': '127.0.0.2', port: 12201 } - ], - hostname: 'server.name', // the name of this host - // (optional, default: os.hostname()) - facility: 'Node.js', // the facility for these log messages - // (optional, default: "Node.js") - bufferSize: 1350 // max UDP packet size, should never exceed the - // MTU of your system (optional, default: 1400) -}); - -logger.on('error', function (error) { - console.error('Error while trying to write to graylog2:', error); -}); -*/ - -// This is just a placeholder function that will be replaced once we get the -// greylog server working. -function log(msg, obj) { - //console.log(msg, obj) -} - -// This is just a placeholder function that will be replaced once we get the -// greylog server working. -function error(msg, obj) { - if (!obj) console.error(msg) - else console.error(msg, obj) -} - -// This is just a placeholder function that will be replaced once we get the -// greylog server working. -function debug(msg, obj) { - //console.log(msg, obj) -} - -module.exports = { - log, - error, - debug -} diff --git a/src/routes/v2/mining.js b/src/routes/v2/mining.js deleted file mode 100644 index 243b75a..0000000 --- a/src/routes/v2/mining.js +++ /dev/null @@ -1,170 +0,0 @@ -"use strict" - -const express = require("express") -const router = express.Router() -const axios = require("axios") - -const routeUtils = require("./route-utils") -const logger = require("./logging.js") -const wlogger = require("../../util/winston-logging") - -// Used to convert error messages to strings, to safely pass to users. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -const BitboxHTTP = axios.create({ - baseURL: process.env.RPC_BASEURL -}) -const username = process.env.RPC_USERNAME -const password = process.env.RPC_PASSWORD - -const requestConfig = { - method: "post", - auth: { - username: username, - password: password - }, - data: { - jsonrpc: "1.0" - } -} - -router.get("/", root) -router.get("/getMiningInfo", getMiningInfo) -router.get("/getNetworkHashps", getNetworkHashPS) - -function root(req, res, next) { - return res.json({ status: "mining" }) -} - -// -// router.get('/getBlockTemplate/:templateRequest', (req, res, next) => { -// BitboxHTTP({ -// method: 'post', -// auth: { -// username: username, -// password: password -// }, -// data: { -// jsonrpc: "1.0", -// id:"getblocktemplate", -// method: "getblocktemplate", -// params: [ -// req.params.templateRequest -// ] -// } -// }) -// .then((response) => { -// res.json(response.data.result); -// }) -// .catch((error) => { -// res.send(error.response.data.error.message); -// }); -// }); - -async function getMiningInfo(req, res, next) { - try { - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "getmininginfo" - requestConfig.data.method = "getmininginfo" - requestConfig.data.params = [] - - const response = await BitboxHTTP(requestConfig) - - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - wlogger.error(`Error in mining.ts/getMiningInfo().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -async function getNetworkHashPS(req, res, next) { - try { - let nblocks = 120 // Default - let height = -1 // Default - if (req.query.nblocks) nblocks = parseInt(req.query.nblocks) - if (req.query.height) height = parseInt(req.query.height) - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "getnetworkhashps" - requestConfig.data.method = "getnetworkhashps" - requestConfig.data.params = [nblocks, height] - - const response = await BitboxHTTP(requestConfig) - - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - wlogger.error(`Error in mining.ts/getNetworkHashPS().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// -// router.post('/submitBlock/:hex', (req, res, next) => { -// let parameters = ''; -// if(req.query.parameters && req.query.parameters !== '') { -// parameters = true; -// } -// -// BitboxHTTP({ -// method: 'post', -// auth: { -// username: username, -// password: password -// }, -// data: { -// jsonrpc: "1.0", -// id:"submitblock", -// method: "submitblock", -// params: [ -// req.params.hex, -// parameters -// ] -// } -// }) -// .then((response) => { -// res.json(response.data.result); -// }) -// .catch((error) => { -// res.send(error.response.data.error.message); -// }); -// }); - -module.exports = { - router, - testableComponents: { - root, - getMiningInfo, - getNetworkHashPS - } -} diff --git a/src/routes/v2/network.js b/src/routes/v2/network.js deleted file mode 100644 index 0af2366..0000000 --- a/src/routes/v2/network.js +++ /dev/null @@ -1,168 +0,0 @@ -"use strict" - -const express = require("express") -const router = express.Router() - -router.get("/", async (req, res, next) => { - res.json({ status: "network" }) -}) - -// router.post('/addNode/:node/:command', (req, res, next) => { -// BITBOX.Network.addNode(req.params.node, req.params.command) -// .then((result) => { -// res.json(result); -// }, (err) => { console.log(err); -// }); -// }); -// -// router.post('/clearBanned', (req, res, next) => { -// BITBOX.Network.clearBanned() -// .then((result) => { -// res.json(result); -// }, (err) => { console.log(err); -// }); -// }); -// -// router.post('/disconnectNode/:address/:nodeid', (req, res, next) => { -// BITBOX.Network.disconnectNode(req.params.address, req.params.nodeid) -// .then((result) => { -// res.json(result); -// }, (err) => { console.log(err); -// }); -// }); -// -// router.get('/getAddedNodeInfo/:node', (req, res, next) => { -// BITBOX.Network.getAddedNodeInfo(req.params.node) -// .then((result) => { -// res.json(result); -// }, (err) => { console.log(err); -// }); -// }); -// -// router.get('/getConnectionCount', (req, res, next) => { -// BitboxHTTP({ -// method: 'post', -// auth: { -// username: username, -// password: password -// }, -// data: { -// jsonrpc: "1.0", -// id:"getconnectioncount", -// method: "getconnectioncount" -// } -// }) -// .then((response) => { -// res.json(response.data.result); -// }) -// .catch((error) => { -// res.send(error.response.data.error.message); -// }); -// }); -// -// router.get('/getNetTotals', (req, res, next) => { -// BitboxHTTP({ -// method: 'post', -// auth: { -// username: username, -// password: password -// }, -// data: { -// jsonrpc: "1.0", -// id:"getnettotals", -// method: "getnettotals" -// } -// }) -// .then((response) => { -// res.json(response.data.result); -// }) -// .catch((error) => { -// res.send(error.response.data.error.message); -// }); -// }); -// -// router.get('/getNetworkInfo', (req, res, next) => { -// BitboxHTTP({ -// method: 'post', -// auth: { -// username: username, -// password: password -// }, -// data: { -// jsonrpc: "1.0", -// id:"getnetworkinfo", -// method: "getnetworkinfo" -// } -// }) -// .then((response) => { -// res.json(response.data.result); -// }) -// .catch((error) => { -// res.send(error.response.data.error.message); -// }); -// }); -// -// router.get('/getPeerInfo', (req, res, next) => { -// BitboxHTTP({ -// method: 'post', -// auth: { -// username: username, -// password: password -// }, -// data: { -// jsonrpc: "1.0", -// id:"getpeerinfo", -// method: "getpeerinfo" -// } -// }) -// .then((response) => { -// res.json(response.data.result); -// }) -// .catch((error) => { -// res.send(error.response.data.error.message); -// }); -// }); -// -// router.get('/ping', (req, res, next) => { -// BitboxHTTP({ -// method: 'post', -// auth: { -// username: username, -// password: password -// }, -// data: { -// jsonrpc: "1.0", -// id:"ping", -// method: "ping" -// } -// }) -// .then((response) => { -// res.json(JSON.stringify(response.data.result)); -// }) -// .catch((error) => { -// res.send(error.response.data.error.message); -// }); -// }); -// -// router.post('/setBan/:subnet/:command', (req, res, next) => { -// // TODO finish this -// BITBOX.Network.getConnectionCount(req.params.subnet, req.params.command) -// .then((result) => { -// res.json(result); -// }, (err) => { console.log(err); -// }); -// }); -// -// router.post('/setNetworkActive/:state', (req, res, next) => { -// let state = true; -// if(req.params.state && req.params.state === 'false') { -// state = false; -// } -// BITBOX.Network.getConnectionCount(state) -// .then((result) => { -// res.json(result); -// }, (err) => { console.log(err); -// }); -// }); - -module.exports = router diff --git a/src/routes/v2/rawtransactions.js b/src/routes/v2/rawtransactions.js deleted file mode 100644 index 99104c7..0000000 --- a/src/routes/v2/rawtransactions.js +++ /dev/null @@ -1,609 +0,0 @@ -"use strict" - -const express = require("express") -const router = express.Router() -const axios = require("axios") - -const routeUtils = require("./route-utils") -const logger = require("./logging.js") -const wlogger = require("../../util/winston-logging") - -// Used to convert error messages to strings, to safely pass to users. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -const BitboxHTTP = axios.create({ - baseURL: process.env.RPC_BASEURL -}) -const username = process.env.RPC_USERNAME -const password = process.env.RPC_PASSWORD - -const requestConfig = { - method: "post", - auth: { - username: username, - password: password - }, - data: { - jsonrpc: "1.0" - } -} - -router.get("/", root) -router.get("/decodeRawTransaction/:hex", decodeRawTransactionSingle) -router.post("/decodeRawTransaction", decodeRawTransactionBulk) -router.get("/decodeScript/:hex", decodeScriptSingle) -router.post("/decodeScript", decodeScriptBulk) -router.post("/getRawTransaction", getRawTransactionBulk) -router.get("/getRawTransaction/:txid", getRawTransactionSingle) -router.post("/sendRawTransaction", sendRawTransactionBulk) -router.get("/sendRawTransaction/:hex", sendRawTransactionSingle) - -function root(req, res, next) { - return res.json({ status: "rawtransactions" }) -} - -// Decode transaction hex into a JSON object. -// GET -async function decodeRawTransactionSingle(req, res, next) { - try { - const hex = req.params.hex - - // Throw an error if hex is empty. - if (!hex || hex === "") { - res.status(400) - return res.json({ error: "hex can not be empty" }) - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "decoderawtransaction" - requestConfig.data.method = "decoderawtransaction" - requestConfig.data.params = [hex] - - const response = await BitboxHTTP(requestConfig) - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error( - `Error in rawtransactions.ts/decodeRawTransactionSingle().`, - err - ) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -async function decodeRawTransactionBulk(req, res, next) { - try { - const hexes = req.body.hexes - - if (!Array.isArray(hexes)) { - res.status(400) - return res.json({ error: "hexes must be an array" }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, hexes)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - const results = [] - - // Validate each element in the address array. - for (let i = 0; i < hexes.length; i++) { - const thisHex = hexes[i] - - // Reject if id is empty - if (!thisHex || thisHex === "") { - res.status(400) - return res.json({ error: "Encountered empty hex" }) - } - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - // Loop through each height and creates an array of requests to call in parallel - const promises = hexes.map(async hex => { - requestConfig.data.id = "decoderawtransaction" - requestConfig.data.method = "decoderawtransaction" - requestConfig.data.params = [hex] - - return await BitboxHTTP(requestConfig) - }) - - // Wait for all parallel Insight requests to return. - const axiosResult = await axios.all(promises) - - // Retrieve the data part of the result. - const result = axiosResult.map(x => x.data.result) - - res.status(200) - return res.json(result) - - /* - // Loop through each hex and creates an array of requests to call in parallel - hexes = hexes.map(async (hex: any) => { - if (!hex || hex === "") { - res.status(400) - return res.json({ error: "Encountered empty hex" }) - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "decoderawtransaction" - requestConfig.data.method = "decoderawtransaction" - requestConfig.data.params = [hex] - - return await BitboxHTTP(requestConfig) - }) - - const result: Array = [] - return axios.all(hexes).then( - axios.spread((...args) => { - args.forEach((arg: any) => { - if (arg) { - result.push(arg.data.result) - } - }) - res.status(200) - return res.json(result) - }) - ) -*/ - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/getRawTransaction: `, err) - wlogger.error( - `Error in rawtransactions.ts/decodeRawTransactionBulk().`, - err - ) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Decode a raw transaction from hex to assembly. -// GET single -async function decodeScriptSingle(req, res, next) { - try { - const hex = req.params.hex - - // Throw an error if hex is empty. - if (!hex || hex === "") { - res.status(400) - return res.json({ error: "hex can not be empty" }) - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "decodescript" - requestConfig.data.method = "decodescript" - requestConfig.data.params = [hex] - - const response = await BitboxHTTP(requestConfig) - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeScript: `, err) - wlogger.error(`Error in rawtransactions.ts/decodeScriptSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Decode a raw transaction from hex to assembly. -// POST bulk -async function decodeScriptBulk(req, res, next) { - try { - const hexes = req.body.hexes - - // Validation - if (!Array.isArray(hexes)) { - res.status(400) - return res.json({ error: "hexes must be an array" }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, hexes)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - // Validate each hex in the array - for (let i = 0; i < hexes.length; i++) { - const hex = hexes[i] - - // Throw an error if hex is empty. - if (!hex || hex === "") { - res.status(400) - return res.json({ error: "Encountered empty hex" }) - } - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - // Loop through each hex and create an array of promises - const promises = hexes.map(async hex => { - requestConfig.data.id = "decodescript" - requestConfig.data.method = "decodescript" - requestConfig.data.params = [hex] - - const response = await BitboxHTTP(requestConfig) - return response - }) - - // Wait for all parallel promises to return. - const resolved = await Promise.all(promises) - - // Retrieve the data from each resolved promise. - const result = resolved.map(x => x.data.result) - - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeScript: `, err) - wlogger.error(`Error in rawtransactions.ts/decodeScriptBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Retrieve raw transactions details from the full node. -async function getRawTransactionsFromNode(txid, verbose) { - try { - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "getrawtransaction" - requestConfig.data.method = "getrawtransaction" - requestConfig.data.params = [txid, verbose] - - const response = await BitboxHTTP(requestConfig) - - return response.data.result - } catch (err) { - wlogger.error(`Error in rawtransactions.ts/getRawTransactionsFromNode().`) - throw err - } -} - -// Get a JSON object breakdown of transaction details. -// POST -async function getRawTransactionBulk(req, res, next) { - try { - let verbose = 0 - if (req.body.verbose) verbose = 1 - - const txids = req.body.txids - if (!Array.isArray(txids)) { - res.status(400) - return res.json({ error: "txids must be an array" }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, txids)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - // stub response object - const returnResponse = { - status: 100, - json: { - error: "" - } - } - - // Validate each txid in the array. - for (let i = 0; i < txids.length; i++) { - const txid = txids[i] - - if (!txid || txid === "") { - res.status(400) - return res.json({ error: `Encountered empty TXID` }) - } - - if (txid.length !== 64) { - res.status(400) - return res.json({ - error: `parameter 1 must be of length 64 (not ${txid.length})` - }) - } - } - - // Loop through each txid and create an array of promises - const promises = txids.map(async txid => - getRawTransactionsFromNode(txid, verbose) - ) - - // Wait for all parallel promises to return. - const axiosResult = await axios.all(promises) - - res.status(200) - return res.json(axiosResult) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/getRawTransaction: `, err) - wlogger.error(`Error in rawtransactions.ts/getRawTransactionBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Get a JSON object breakdown of transaction details. -// GET -async function getRawTransactionSingle(req, res, next) { - try { - let verbose = 0 - if (req.query.verbose === "true") verbose = 1 - - const txid = req.params.txid - if (!txid || txid === "") { - res.status(400) - return res.json({ error: "txid can not be empty" }) - } - - const data = await getRawTransactionsFromNode(txid, verbose) - - return res.json(data) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/getRawTransaction: `, err) - wlogger.error(`Error in rawtransactions.ts/getRawTransactionSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Transmit a raw transaction to the BCH network. -async function sendRawTransactionBulk(req, res, next) { - try { - // Validation - const hexes = req.body.hexes - - // Reject if input is not an array - if (!Array.isArray(hexes)) { - res.status(400) - return res.json({ error: "hex must be an array" }) - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, hexes)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - // Validate each element - for (let i = 0; i < hexes.length; i++) { - const hex = hexes[i] - - if (hex === "") { - res.status(400) - return res.json({ - error: `Encountered empty hex` - }) - } - } - - // Dev Note CT 1/31/2019: - // Sending the 'sendrawtrnasaction' RPC call to a full node in parallel will - // not work. Testing showed that the full node will return the same TXID for - // different TX hexes. I believe this is by design, to prevent double spends. - // In parallel, we are essentially asking the node to broadcast a new TX before - // it's finished broadcast the previous one. Serial execution is required. - - // How to send TX hexes in parallel the WRONG WAY: - /* - // Collect an array of promises. - const promises = hexes.map(async (hex: any) => { - requestConfig.data.id = "sendrawtransaction" - requestConfig.data.method = "sendrawtransaction" - requestConfig.data.params = [hex] - - return await BitboxHTTP(requestConfig) - }) - - // Wait for all parallel Insight requests to return. - const axiosResult: Array = await axios.all(promises) - - // Retrieve the data part of the result. - const result = axiosResult.map(x => x.data.result) - */ - - // Sending them serially. - const result = [] - for (let i = 0; i < hexes.length; i++) { - const hex = hexes[i] - - requestConfig.data.id = "sendrawtransaction" - requestConfig.data.method = "sendrawtransaction" - requestConfig.data.params = [hex] - - const rpcResult = await BitboxHTTP(requestConfig) - - result.push(rpcResult.data.result) - } - - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - wlogger.error(`Error in rawtransactions.ts/sendRawTransactionBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Transmit a raw transaction to the BCH network. -async function sendRawTransactionSingle(req, res, next) { - try { - const hex = req.params.hex // URL parameter - - // Reject if input is not an array or a string - if (typeof hex !== "string") { - res.status(400) - return res.json({ error: "hex must be a string" }) - } - - // Validation - if (hex === "") { - res.status(400) - return res.json({ - error: `Encountered empty hex` - }) - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - // RPC call - requestConfig.data.id = "sendrawtransaction" - requestConfig.data.method = "sendrawtransaction" - requestConfig.data.params = [hex] - - const rpcResult = await BitboxHTTP(requestConfig) - - const result = rpcResult.data.result - - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - wlogger.error( - `Error in rawtransactions.ts/sendRawTransactionSingle().`, - err - ) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -module.exports = { - router, - getRawTransactionsFromNode, - testableComponents: { - root, - decodeRawTransactionSingle, - decodeRawTransactionBulk, - decodeScriptSingle, - decodeScriptBulk, - getRawTransactionBulk, - getRawTransactionSingle, - sendRawTransactionBulk, - sendRawTransactionSingle - } -} diff --git a/src/routes/v2/route-utils.js b/src/routes/v2/route-utils.js deleted file mode 100644 index 4b462c6..0000000 --- a/src/routes/v2/route-utils.js +++ /dev/null @@ -1,139 +0,0 @@ -/* - A private library of utility functions used by several different routes. -*/ - -"use strict" - -const axios = require("axios") -const wlogger = require("../../util/winston-logging") - -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -const BITBOXJS = require("@chris.troutner/bch-js") -const BITBOX = new BITBOXJS() - -module.exports = { - validateNetwork, // Prevents a common user error - setEnvVars, // Allows RPC variables to be set dynamically based on changing env vars. - decodeError, // Extract and interpret error messages. - validateArraySize // Ensure the passed array meets rate limiting requirements. -} - -// This function expects the Request Express.js object and an array as input. -// The array is then validated against freemium and pro-tier rate limiting -// requirements. A boolean is returned to indicate if the array size if valid -// or not. -function validateArraySize(req, array) { - const FREEMIUM_INPUT_SIZE = 20 - const PRO_INPUT_SIZE = 100 - - if (req.locals && req.locals.proLimit) { - if (array.length <= PRO_INPUT_SIZE) return true - } else if (array.length <= FREEMIUM_INPUT_SIZE) { - return true - } - - return false -} - -// Returns true if user-provided cash address matches the correct network, -// mainnet or testnet. If NETWORK env var is not defined, it returns false. -// This prevent a common user-error issue that is easy to make: passing a -// testnet address into rest.bitcoin.com or passing a mainnet address into -// trest.bitcoin.com. -function validateNetwork(addr) { - try { - const network = process.env.NETWORK - - // Return false if NETWORK is not defined. - if (!network || network === "") { - console.log(`Warning: NETWORK environment variable is not defined!`) - return false - } - - // Convert the user-provided address to a cashaddress, for easy detection - // of the intended network. - const cashAddr = BITBOX.Address.toCashAddress(addr) - - // Return true if the network and address both match testnet - const addrIsTest = BITBOX.Address.isTestnetAddress(cashAddr) - if (network === "testnet" && addrIsTest) return true - - // Return true if the network and address both match mainnet - const addrIsMain = BITBOX.Address.isMainnetAddress(cashAddr) - if (network === "mainnet" && addrIsMain) return true - - return false - } catch (err) { - logger.error(`Error in validateNetwork()`) - return false - } -} - -// Dynamically set these based on env vars. Allows unit testing. -function setEnvVars() { - const BitboxHTTP = axios.create({ - baseURL: process.env.RPC_BASEURL - }) - const username = process.env.RPC_USERNAME - const password = process.env.RPC_PASSWORD - - const requestConfig = { - method: "post", - auth: { - username: username, - password: password - }, - data: { - jsonrpc: "1.0" - } - } - - return { BitboxHTTP, username, password, requestConfig } -} - -// Error messages returned by a full node can be burried pretty deep inside the -// error object returned by Axios. This function attempts to extract and interpret -// error messages. -// Returns an object. If successful, obj.msg is a string. -// If there is a failure, obj.msg is false. -function decodeError(err) { - try { - // Attempt to extract the full node error message. - if ( - err.response && - err.response.data && - err.response.data.error && - err.response.data.error.message - ) - return { msg: err.response.data.error.message, status: 400 } - - // Attempt to extract the Insight error message - if (err.response && err.response.data) - return { msg: err.response.data, status: err.response.status } - - // Attempt to detect a network connection error. - if (err.message && err.message.indexOf("ENOTFOUND") > -1) { - return { - msg: - "Network error: Could not communicate with full node or other external service.", - status: 503 - } - } - - // Different kind of network error - if (err.message && err.message.indexOf("ENETUNREACH") > -1) { - return { - msg: - "Network error: Could not communicate with full node or other external service.", - status: 503 - } - } - - return { msg: false, status: 500 } - } catch (err) { - wlogger.error(`unhandled error in route-utils.js/decodeError(): `, err) - return { msg: false, status: 500 } - } -} diff --git a/src/routes/v2/services/bitbox.js b/src/routes/v2/services/bitbox.js deleted file mode 100644 index 8fb0414..0000000 --- a/src/routes/v2/services/bitbox.js +++ /dev/null @@ -1,11 +0,0 @@ -"use strict" - -const axios = require("axios") - -const BitboxHTTP = axios.create({ - baseURL: process.env.RPC_BASEURL -}) - -const getInstance = () => BitboxHTTP - -module.exports = getInstance diff --git a/src/routes/v2/services/requestUtils.js b/src/routes/v2/services/requestUtils.js deleted file mode 100644 index 7f64965..0000000 --- a/src/routes/v2/services/requestUtils.js +++ /dev/null @@ -1,20 +0,0 @@ -"use strict" - -const username = process.env.RPC_USERNAME -const password = process.env.RPC_PASSWORD - -const getRequestConfig = (method, params) => ({ - method: "post", - auth: { - username, - password - }, - data: { - jsonrpc: "1.0", - id: method, - method, - params - } -}) - -module.exports = getRequestConfig diff --git a/src/routes/v2/slp.js b/src/routes/v2/slp.js deleted file mode 100644 index af9cdb0..0000000 --- a/src/routes/v2/slp.js +++ /dev/null @@ -1,1369 +0,0 @@ -"use strict" - -const express = require("express") -const router = express.Router() -const axios = require("axios") - -const routeUtils = require("./route-utils") -const logger = require("./logging.js") -const strftime = require("strftime") -const wlogger = require("../../util/winston-logging") - -// Used to convert error messages to strings, to safely pass to users. -const util = require("util") -util.inspect.defaultOptions = { depth: 5 } - -const SLPSDK = require("slp-sdk") -const SLP = new SLPSDK() - -// Instantiate SLPJS. -const slp = require("slpjs") -const slpjs = new slp.Slp(SLP) -const utils = slp.Utils - -// SLP tx db (LevelDB for caching) -const level = require("level") -const slpTxDb = level("./slp-tx-db") - -// Setup JSON RPC -const BitboxHTTP = axios.create({ - baseURL: process.env.RPC_BASEURL -}) -const username = process.env.RPC_USERNAME -const password = process.env.RPC_PASSWORD - -// Setup REST and TREST URLs used by slpjs -// Dev note: this allows for unit tests to mock the URL. -if (!process.env.REST_URL) process.env.REST_URL = `https://rest.bitcoin.com/v2/` -if (!process.env.TREST_URL) - process.env.TREST_URL = `https://trest.bitcoin.com/v2/` - -router.get("/", root) -router.get("/list", list) -router.get("/list/:tokenId", listSingleToken) -router.post("/list", listBulkToken) -router.get("/balancesForAddress/:address", balancesForAddress) -router.get("/balancesForToken/:tokenId", balancesForTokenSingle) -router.get("/balance/:address/:tokenId", balancesForAddressByTokenID) -router.get("/convert/:address", convertAddressSingle) -router.post("/convert", convertAddressBulk) -router.post("/validateTxid", validateBulk) -router.get("/validateTxid/:txid", validateSingle) -router.get("/txDetails/:txid", txDetails) -router.get("/tokenStats/:tokenId", tokenStats) -router.get("/transactions/:tokenId/:address", txsTokenIdAddressSingle) - -if (process.env.NON_JS_FRAMEWORK && process.env.NON_JS_FRAMEWORK === "true") { - router.get( - "/createTokenType1/:fundingAddress/:fundingWif/:tokenReceiverAddress/:batonReceiverAddress/:bchChangeReceiverAddress/:decimals/:name/:symbol/:documentUri/:documentHash/:initialTokenQty", - createTokenType1 - ) - router.get( - "/mintTokenType1/:fundingAddress/:fundingWif/:tokenReceiverAddress/:batonReceiverAddress/:bchChangeReceiverAddress/:tokenId/:additionalTokenQty", - mintTokenType1 - ) - router.get( - "/sendTokenType1/:fundingAddress/:fundingWif/:tokenReceiverAddress/:bchChangeReceiverAddress/:tokenId/:amount", - sendTokenType1 - ) - router.get( - "/burnTokenType1/:fundingAddress/:fundingWif/:bchChangeReceiverAddress/:tokenId/:amount", - burnTokenType1 - ) - router.get( - "/burnAllTokenType1/:fundingAddress/:fundingWif/:bchChangeReceiverAddress/:tokenId", - burnAllTokenType1 - ) -} - -// Retrieve raw transactions details from the full node. -// TODO: move this function to a separate support library. -// TODO: Add unit tests for this function. -async function getRawTransactionsFromNode(txids) { - try { - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - const txPromises = txids.map(async txid => { - // Check slpTxDb - try { - if (slpTxDb.isOpen()) { - const rawTx = await slpTxDb.get(txid) - return rawTx - } - } catch (err) {} - - requestConfig.data.id = "getrawtransaction" - requestConfig.data.method = "getrawtransaction" - requestConfig.data.params = [txid, 0] - - const response = await BitboxHTTP(requestConfig) - const result = response.data.result - - // Insert to slpTxDb - try { - if (slpTxDb.isOpen()) await slpTxDb.put(txid, result) - } catch (err) { - // console.log("Error inserting to slpTxDb", err) - } - - return result - }) - - const results = await axios.all(txPromises) - return results - } catch (err) { - wlogger.error(`Error in slp.ts/getRawTransactionsFromNode().`, err) - throw err - } -} - -// Create a validator for validating SLP transactions. -function createValidator(network, getRawTransactions = null) { - let tmpSLP - - if (network === "mainnet") - tmpSLP = new SLPSDK({ restURL: process.env.REST_URL }) - else tmpSLP = new SLPSDK({ restURL: process.env.TREST_URL }) - - const slpValidator = new slp.LocalValidator( - tmpSLP, - getRawTransactions - ? getRawTransactions - : tmpSLP.RawTransactions.getRawTransaction.bind(this) - ) - - return slpValidator -} - -// Instantiate the local SLP validator. -const slpValidator = createValidator( - process.env.NETWORK, - getRawTransactionsFromNode -) - -// Instantiate the bitboxproxy class in SLPJS. -const bitboxproxy = new slp.BitboxNetwork(SLP, slpValidator) - -const requestConfig = { - method: "post", - auth: { - username: username, - password: password - }, - data: { - jsonrpc: "1.0" - } -} - -function formatTokenOutput(token) { - token.tokenDetails.id = token.tokenDetails.tokenIdHex - delete token.tokenDetails.tokenIdHex - token.tokenDetails.documentHash = token.tokenDetails.documentSha256Hex - delete token.tokenDetails.documentSha256Hex - token.tokenDetails.initialTokenQty = parseFloat( - token.tokenDetails.genesisOrMintQuantity - ) - delete token.tokenDetails.genesisOrMintQuantity - delete token.tokenDetails.transactionType - delete token.tokenDetails.batonVout - delete token.tokenDetails.sendOutputs - - token.tokenDetails.blockCreated = token.tokenStats.block_created - token.tokenDetails.blockLastActiveSend = - token.tokenStats.block_last_active_send - token.tokenDetails.blockLastActiveMint = - token.tokenStats.block_last_active_mint - token.tokenDetails.txnsSinceGenesis = - token.tokenStats.qty_valid_txns_since_genesis - token.tokenDetails.validAddresses = token.tokenStats.qty_valid_token_addresses - token.tokenDetails.totalMinted = parseFloat(token.tokenStats.qty_token_minted) - token.tokenDetails.totalBurned = parseFloat(token.tokenStats.qty_token_burned) - token.tokenDetails.circulatingSupply = parseFloat( - token.tokenStats.qty_token_circulating_supply - ) - token.tokenDetails.mintingBatonStatus = token.tokenStats.minting_baton_status - - delete token.tokenStats.block_last_active_send - delete token.tokenStats.block_last_active_mint - delete token.tokenStats.qty_valid_txns_since_genesis - delete token.tokenStats.qty_valid_token_addresses - return token -} - -function root(req, res, next) { - return res.json({ status: "slp" }) -} - -async function list(req, res, next) { - try { - const query = { - v: 3, - q: { - db: ["t"], - find: { - $query: {} - }, - project: { tokenDetails: 1, tokenStats: 1, _id: 0 }, - sort: { "tokenStats.block_created": -1 }, - limit: 10000 - } - } - - const s = JSON.stringify(query) - const b64 = Buffer.from(s).toString("base64") - const url = `${process.env.SLPDB_URL}q/${b64}` - - // Get data from SLPDB. - const tokenRes = await axios.get(url) - - const formattedTokens = [] - - if (tokenRes.data.t.length) { - tokenRes.data.t.forEach(token => { - token = formatTokenOutput(token) - formattedTokens.push(token.tokenDetails) - }) - } - - res.status(200) - return res.json(formattedTokens) - } catch (err) { - wlogger.error(`Error in slp.ts/list().`, err) - - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - res.status(500) - return res.json({ error: `Error in /list: ${err.message}` }) - } -} - -async function listSingleToken(req, res, next) { - try { - const tokenId = req.params.tokenId - - if (!tokenId || tokenId === "") { - res.status(400) - return res.json({ error: "tokenId can not be empty" }) - } - - const t = await lookupToken(tokenId) - - res.status(200) - return res.json(t) - } catch (err) { - wlogger.error(`Error in slp.ts/listSingleToken().`, err) - - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - res.status(500) - return res.json({ error: `Error in /list/:tokenId: ${err.message}` }) - } -} - -async function listBulkToken(req, res, next) { - try { - const tokenIds = req.body.tokenIds - - // Reject if tokenIds is not an array. - if (!Array.isArray(tokenIds)) { - res.status(400) - return res.json({ - error: "tokenIds needs to be an array. Use GET for single tokenId." - }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, tokenIds)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - const query = { - v: 3, - q: { - db: ["t"], - find: { - "tokenDetails.tokenIdHex": { - $in: tokenIds - } - }, - project: { tokenDetails: 1, tokenStats: 1, _id: 0 }, - sort: { "tokenStats.block_created": -1 }, - limit: 10000 - } - } - - const s = JSON.stringify(query) - const b64 = Buffer.from(s).toString("base64") - const url = `${process.env.SLPDB_URL}q/${b64}` - - const tokenRes = await axios.get(url) - - const formattedTokens = [] - const txids = [] - - if (tokenRes.data.t.length) { - tokenRes.data.t.forEach(token => { - txids.push(token.tokenDetails.tokenIdHex) - token = formatTokenOutput(token) - formattedTokens.push(token.tokenDetails) - }) - } - - tokenIds.forEach(tokenId => { - if (!txids.includes(tokenId)) { - formattedTokens.push({ - id: tokenId, - valid: false - }) - } - }) - - res.status(200) - return res.json(formattedTokens) - } catch (err) { - wlogger.error(`Error in slp.ts/listBulkToken().`, err) - - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - res.status(500) - return res.json({ error: `Error in /list/:tokenId: ${err.message}` }) - } -} - -async function lookupToken(tokenId) { - try { - const query = { - v: 3, - q: { - db: ["t"], - find: { - $query: { - "tokenDetails.tokenIdHex": tokenId - } - }, - project: { tokenDetails: 1, tokenStats: 1, _id: 0 }, - limit: 1000 - } - } - - const s = JSON.stringify(query) - const b64 = Buffer.from(s).toString("base64") - const url = `${process.env.SLPDB_URL}q/${b64}` - - const tokenRes = await axios.get(url) - //console.log(`tokenRes.data: ${util.inspect(tokenRes.data,null,2)}`) - //console.log(`tokenRes.data.t[0]: ${util.inspect(tokenRes.data.t[0],null,2)}`) - - const formattedTokens = [] - - if (tokenRes.data.t.length) { - tokenRes.data.t.forEach(token => { - token = formatTokenOutput(token) - formattedTokens.push(token.tokenDetails) - }) - } - - let t - formattedTokens.forEach(token => { - if (token.id === tokenId) t = token - }) - - // If token could not be found. - if (t === undefined) { - t = { - id: "not found" - } - } - - return t - } catch (err) { - wlogger.error(`Error in slp.ts/lookupToken().`, err) - //console.log(`Error in slp.ts/lookupToken()`) - throw err - } -} - -// Retrieve token balances for all tokens for a single address. -async function balancesForAddress(req, res, next) { - try { - // Validate the input data. - const address = req.params.address - if (!address || address === "") { - res.status(400) - return res.json({ error: "address can not be empty" }) - } - - // Ensure the input is a valid BCH address. - try { - const cash = utils.toCashAddress(address) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${address}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const cashAddr = utils.toCashAddress(address) - const networkIsValid = routeUtils.validateNetwork(cashAddr) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - - const query = { - v: 3, - q: { - db: ["a"], - find: { - address: SLP.Address.toSLPAddress(address), - token_balance: { $gte: 0 } - }, - limit: 10000 - } - } - - const s = JSON.stringify(query) - const b64 = Buffer.from(s).toString("base64") - const url = `${process.env.SLPDB_URL}q/${b64}` - - const tokenRes = await axios.get(url) - - const tokenIds = [] - if (tokenRes.data.a.length > 0) { - tokenRes.data.a = tokenRes.data.a.map(token => { - token.tokenId = token.tokenDetails.tokenIdHex - tokenIds.push(token.tokenId) - token.balance = parseFloat(token.token_balance) - token.slpAddress = token.address - delete token.tokenDetails - delete token.satoshis_balance - delete token.token_balance - delete token._id - delete token.address - return token - }) - - const promises = tokenIds.map(async tokenId => { - try { - const query2 = { - v: 3, - q: { - db: ["t"], - find: { - $query: { - "tokenDetails.tokenIdHex": tokenId - } - }, - project: { - "tokenDetails.decimals": 1, - "tokenDetails.tokenIdHex": 1, - _id: 0 - }, - limit: 1000 - } - } - - const s2 = JSON.stringify(query2) - const b642 = Buffer.from(s2).toString("base64") - const url2 = `${process.env.SLPDB_URL}q/${b642}` - - const tokenRes2 = await axios.get(url2) - return tokenRes2.data - } catch (err) { - throw err - } - }) - - const details = await axios.all(promises) - tokenRes.data.a = tokenRes.data.a.map(token => { - details.forEach(detail => { - if (detail.t[0].tokenDetails.tokenIdHex === token.tokenId) - token.decimalCount = detail.t[0].tokenDetails.decimals - }) - return token - }) - - return res.json(tokenRes.data.a) - } - return res.json("No balance for this address") - } catch (err) { - wlogger.error(`Error in slp.ts/balancesForAddress().`, err) - - // Decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - res.status(500) - return res.json({ - error: `Error in /ddress/:address: ${err.message}` - }) - } -} - -// Retrieve token balances for all addresses by single tokenId. -async function balancesForTokenSingle(req, res, next) { - try { - // Validate the input data. - const tokenId = req.params.tokenId - if (!tokenId || tokenId === "") { - res.status(400) - return res.json({ error: "tokenId can not be empty" }) - } - - const query = { - v: 3, - q: { - find: { - "tokenDetails.tokenIdHex": tokenId, - token_balance: { $gte: 0 } - }, - limit: 10000, - project: { address: 1, satoshis_balance: 1, token_balance: 1, _id: 0 } - } - } - - const s = JSON.stringify(query) - const b64 = Buffer.from(s).toString("base64") - const url = `${process.env.SLPDB_URL}q/${b64}` - - // Get data from SLPDB. - const tokenRes = await axios.get(url) - const resBalances = tokenRes.data.a.map((addy, index) => { - delete addy.satoshis_balance - addy.tokenBalance = parseFloat(addy.token_balance) - addy.slpAddress = addy.address - addy.tokenId = tokenId - delete addy.address - delete addy.token_balance - return addy - }) - return res.json(resBalances) - } catch (err) { - wlogger.error(`Error in slp.ts/balancesForTokenSingle().`, err) - - // Decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - res.status(500) - return res.json({ - error: `Error in /balancesForToken/:tokenId: ${err.message}` - }) - } -} - -// Retrieve token balances for a single token class, for a single address. -async function balancesForAddressByTokenID(req, res, next) { - try { - // Validate input data. - const address = req.params.address - if (!address || address === "") { - res.status(400) - return res.json({ error: "address can not be empty" }) - } - - const tokenId = req.params.tokenId - if (!tokenId || tokenId === "") { - res.status(400) - return res.json({ error: "tokenId can not be empty" }) - } - - // Ensure the input is a valid BCH address. - try { - const cash = utils.toCashAddress(address) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${address}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const cashAddr = utils.toCashAddress(address) - const networkIsValid = routeUtils.validateNetwork(cashAddr) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - - // Convert input to an simpleledger: address. - const slpAddr = utils.toSlpAddress(req.params.address) - - const query = { - v: 3, - q: { - db: ["a"], - find: { - address: slpAddr, - token_balance: { $gte: 0 } - }, - limit: 10 - } - } - - const s = JSON.stringify(query) - const b64 = Buffer.from(s).toString("base64") - const url = `${process.env.SLPDB_URL}q/${b64}` - - // Get data from SLPDB. - const tokenRes = await axios.get(url) - let resVal - res.status(200) - if (tokenRes.data.a.length > 0) { - tokenRes.data.a.forEach(async token => { - if (token.tokenDetails.tokenIdHex === tokenId) { - resVal = { - tokenId: tokenRes.data.a[0].tokenDetails.tokenIdHex, - balance: parseFloat(tokenRes.data.a[0].token_balance) - } - // const query2 = { - // v: 3, - // q: { - // db: ["t"], - // find: { - // $query: { - // "tokenDetails.tokenIdHex": tokenId - // } - // }, - // project: { - // "tokenDetails.decimals": 1, - // "tokenDetails.tokenIdHex": 1, - // _id: 0 - // }, - // limit: 1000 - // } - // } - // - // const s2 = JSON.stringify(query2) - // const b642 = Buffer.from(s2).toString("base64") - // const url2 = `${process.env.SLPDB_URL}q/${b642}` - // - // const tokenRes2 = await axios.get(url2) - // console.log("hello world", tokenRes2.data.t) - // resVal = { - // tokenId: token.tokenDetails.tokenIdHex, - // balance: parseFloat(token.token_balance), - // decimalCount: tokenRes2.data.t[0].tokenDetails.decimals - // } - // console.log("resVal", resVal) - // return res.json(resVal) - } else { - resVal = { - tokenId: tokenId, - balance: 0 - } - } - }) - } else { - resVal = { - tokenId: tokenId, - balance: 0 - } - } - return res.json(resVal) - } catch (err) { - wlogger.error(`Error in slp.ts/balancesForAddressByTokenID().`, err) - - // Decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - res.status(500) - return res.json({ - error: `Error in /balance/:address/:tokenId: ${err.message}` - }) - } -} - -async function convertAddressSingle(req, res, next) { - try { - const address = req.params.address - - // Validate input - if (!address || address === "") { - res.status(400) - return res.json({ error: "address can not be empty" }) - } - - const slpAddr = SLP.Address.toSLPAddress(address) - - const obj = { - slpAddress: "", - cashAddress: "", - legacyAddress: "" - } - obj.slpAddress = slpAddr - obj.cashAddress = SLP.Address.toCashAddress(slpAddr) - obj.legacyAddress = SLP.Address.toLegacyAddress(obj.cashAddress) - - res.status(200) - return res.json(obj) - } catch (err) { - wlogger.error(`Error in slp.ts/convertAddressSingle().`, err) - - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - res.status(500) - return res.json({ - error: `Error in /address/convert/:address: ${err.message}` - }) - } -} - -async function convertAddressBulk(req, res, next) { - const addresses = req.body.addresses - - // Reject if hashes is not an array. - if (!Array.isArray(addresses)) { - res.status(400) - return res.json({ - error: "addresses needs to be an array. Use GET for single address." - }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, addresses)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - // Convert each address in the array. - const convertedAddresses = [] - for (let i = 0; i < addresses.length; i++) { - const address = addresses[i] - - // Validate input - if (!address || address === "") { - res.status(400) - return res.json({ error: "address can not be empty" }) - } - - const slpAddr = SLP.Address.toSLPAddress(address) - - const obj = { - slpAddress: "", - cashAddress: "", - legacyAddress: "" - } - obj.slpAddress = slpAddr - obj.cashAddress = SLP.Address.toCashAddress(slpAddr) - obj.legacyAddress = SLP.Address.toLegacyAddress(obj.cashAddress) - - convertedAddresses.push(obj) - } - - res.status(200) - return res.json(convertedAddresses) -} - -async function validateBulk(req, res, next) { - try { - const txids = req.body.txids - - // Reject if txids is not an array. - if (!Array.isArray(txids)) { - res.status(400) - return res.json({ error: "txids needs to be an array" }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, txids)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - logger.debug(`Executing slp/validate with these txids: `, txids) - - // Validate each txid - const validatePromises = txids.map(async txid => { - try { - // Dev note: must call module.exports to allow stubs in unit tests. - const isValid = await module.exports.testableComponents.isValidSlpTxid( - txid - ) - - const tmp = { - txid: txid, - valid: isValid ? true : false - } - return tmp - } catch (err) { - //console.log(`err obj: ${util.inspect(err)}`) - //console.log(`err.response.data: ${util.inspect(err.response.data)}`) - throw err - } - }) - - // Filter array to only valid txid results - const validateResults = await axios.all(validatePromises) - const validTxids = validateResults.filter(result => result) - - res.status(200) - return res.json(validTxids) - } catch (err) { - wlogger.error(`Error in slp.ts/validateBulk().`, err) - - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -async function validateSingle(req, res, next) { - try { - const txid = req.params.txid - - // Validate input - if (!txid || txid === "") { - res.status(400) - return res.json({ error: "txid can not be empty" }) - } - - logger.debug(`Executing slp/validate/:txid with this txid: `, txid) - - // Validate txid - // Dev note: must call module.exports to allow stubs in unit tests. - const isValid = await module.exports.testableComponents.isValidSlpTxid(txid) - - const tmp = { - txid: txid, - valid: isValid ? true : false - } - - res.status(200) - return res.json(tmp) - } catch (err) { - wlogger.error(`Error in slp.ts/validateSingle().`, err) - - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Returns a Boolean if the input TXID is a valid SLP TXID. -async function isValidSlpTxid(txid) { - const isValid = await slpValidator.isValidSlpTxid(txid) - return isValid -} - -// Below are functions which are enabled for teams not using our javascript SDKs which still need to create txs -// These should never be enabled on our public REST API - -async function createTokenType1(req, res, next) { - const fundingAddress = req.params.fundingAddress - if (!fundingAddress || fundingAddress === "") { - res.status(400) - return res.json({ error: "fundingAddress can not be empty" }) - } - - const fundingWif = req.params.fundingWif - if (!fundingWif || fundingWif === "") { - res.status(400) - return res.json({ error: "fundingWif can not be empty" }) - } - - const tokenReceiverAddress = req.params.tokenReceiverAddress - if (!tokenReceiverAddress || tokenReceiverAddress === "") { - res.status(400) - return res.json({ error: "tokenReceiverAddress can not be empty" }) - } - - const batonReceiverAddress = req.params.batonReceiverAddress - if (!batonReceiverAddress || batonReceiverAddress === "") { - res.status(400) - return res.json({ error: "batonReceiverAddress can not be empty" }) - } - - const bchChangeReceiverAddress = req.params.bchChangeReceiverAddress - if (!bchChangeReceiverAddress || bchChangeReceiverAddress === "") { - res.status(400) - return res.json({ error: "bchChangeReceiverAddress can not be empty" }) - } - - const decimals = req.params.decimals - if (!decimals || decimals === "") { - res.status(400) - return res.json({ error: "decimals can not be empty" }) - } - - const name = req.params.name - if (!name || name === "") { - res.status(400) - return res.json({ error: "name can not be empty" }) - } - - const symbol = req.params.symbol - if (!symbol || symbol === "") { - res.status(400) - return res.json({ error: "symbol can not be empty" }) - } - - const documentUri = req.params.documentUri - if (!documentUri || documentUri === "") { - res.status(400) - return res.json({ error: "documentUri can not be empty" }) - } - - const documentHash = req.params.documentHash - if (!documentHash || documentHash === "") { - res.status(400) - return res.json({ error: "documentHash can not be empty" }) - } - - const initialTokenQty = req.params.initialTokenQty - if (!initialTokenQty || initialTokenQty === "") { - res.status(400) - return res.json({ error: "initialTokenQty can not be empty" }) - } - - const token = await SLP.TokenType1.create({ - fundingAddress: fundingAddress, - fundingWif: fundingWif, - tokenReceiverAddress: tokenReceiverAddress, - batonReceiverAddress: batonReceiverAddress, - bchChangeReceiverAddress: bchChangeReceiverAddress, - decimals: decimals, - name: name, - symbol: symbol, - documentUri: documentUri, - documentHash: documentHash, - initialTokenQty: initialTokenQty - }) - - res.status(200) - return res.json(token) -} - -async function mintTokenType1(req, res, next) { - const fundingAddress = req.params.fundingAddress - if (!fundingAddress || fundingAddress === "") { - res.status(400) - return res.json({ error: "fundingAddress can not be empty" }) - } - - const fundingWif = req.params.fundingWif - if (!fundingWif || fundingWif === "") { - res.status(400) - return res.json({ error: "fundingWif can not be empty" }) - } - - const tokenReceiverAddress = req.params.tokenReceiverAddress - if (!tokenReceiverAddress || tokenReceiverAddress === "") { - res.status(400) - return res.json({ error: "tokenReceiverAddress can not be empty" }) - } - - const batonReceiverAddress = req.params.batonReceiverAddress - if (!batonReceiverAddress || batonReceiverAddress === "") { - res.status(400) - return res.json({ error: "batonReceiverAddress can not be empty" }) - } - - const bchChangeReceiverAddress = req.params.bchChangeReceiverAddress - if (!bchChangeReceiverAddress || bchChangeReceiverAddress === "") { - res.status(400) - return res.json({ error: "bchChangeReceiverAddress can not be empty" }) - } - - const tokenId = req.params.tokenId - if (!tokenId || tokenId === "") { - res.status(400) - return res.json({ error: "tokenId can not be empty" }) - } - - const additionalTokenQty = req.params.additionalTokenQty - if (!additionalTokenQty || additionalTokenQty === "") { - res.status(400) - return res.json({ error: "additionalTokenQty can not be empty" }) - } - - const mint = await SLP.TokenType1.mint({ - fundingAddress: fundingAddress, - fundingWif: fundingWif, - tokenReceiverAddress: tokenReceiverAddress, - batonReceiverAddress: batonReceiverAddress, - bchChangeReceiverAddress: bchChangeReceiverAddress, - tokenId: tokenId, - additionalTokenQty: additionalTokenQty - }) - - res.status(200) - return res.json(mint) -} - -async function sendTokenType1(req, res, next) { - const fundingAddress = req.params.fundingAddress - if (!fundingAddress || fundingAddress === "") { - res.status(400) - return res.json({ error: "fundingAddress can not be empty" }) - } - - const fundingWif = req.params.fundingWif - if (!fundingWif || fundingWif === "") { - res.status(400) - return res.json({ error: "fundingWif can not be empty" }) - } - - const tokenReceiverAddress = req.params.tokenReceiverAddress - if (!tokenReceiverAddress || tokenReceiverAddress === "") { - res.status(400) - return res.json({ error: "tokenReceiverAddress can not be empty" }) - } - - const bchChangeReceiverAddress = req.params.bchChangeReceiverAddress - if (!bchChangeReceiverAddress || bchChangeReceiverAddress === "") { - res.status(400) - return res.json({ error: "bchChangeReceiverAddress can not be empty" }) - } - - const tokenId = req.params.tokenId - if (!tokenId || tokenId === "") { - res.status(400) - return res.json({ error: "tokenId can not be empty" }) - } - - const amount = req.params.amount - if (!amount || amount === "") { - res.status(400) - return res.json({ error: "amount can not be empty" }) - } - const send = await SLP.TokenType1.send({ - fundingAddress: fundingAddress, - fundingWif: fundingWif, - tokenReceiverAddress: tokenReceiverAddress, - bchChangeReceiverAddress: bchChangeReceiverAddress, - tokenId: tokenId, - amount: amount - }) - - res.status(200) - return res.json(send) -} - -async function burnTokenType1(req, res, next) { - const fundingAddress = req.params.fundingAddress - if (!fundingAddress || fundingAddress === "") { - res.status(400) - return res.json({ error: "fundingAddress can not be empty" }) - } - - const fundingWif = req.params.fundingWif - if (!fundingWif || fundingWif === "") { - res.status(400) - return res.json({ error: "fundingWif can not be empty" }) - } - - const bchChangeReceiverAddress = req.params.bchChangeReceiverAddress - if (!bchChangeReceiverAddress || bchChangeReceiverAddress === "") { - res.status(400) - return res.json({ error: "bchChangeReceiverAddress can not be empty" }) - } - - const tokenId = req.params.tokenId - if (!tokenId || tokenId === "") { - res.status(400) - return res.json({ error: "tokenId can not be empty" }) - } - - const amount = req.params.amount - if (!amount || amount === "") { - res.status(400) - return res.json({ error: "amount can not be empty" }) - } - - const burn = await SLP.TokenType1.burn({ - fundingAddress: fundingAddress, - fundingWif: fundingWif, - tokenId: tokenId, - amount: amount, - bchChangeReceiverAddress: bchChangeReceiverAddress - }) - - res.status(200) - return res.json(burn) -} - -async function burnAllTokenType1(req, res, next) { - const fundingAddress = req.params.fundingAddress - if (!fundingAddress || fundingAddress === "") { - res.status(400) - return res.json({ error: "fundingAddress can not be empty" }) - } - - const fundingWif = req.params.fundingWif - if (!fundingWif || fundingWif === "") { - res.status(400) - return res.json({ error: "fundingWif can not be empty" }) - } - - const bchChangeReceiverAddress = req.params.bchChangeReceiverAddress - if (!bchChangeReceiverAddress || bchChangeReceiverAddress === "") { - res.status(400) - return res.json({ error: "bchChangeReceiverAddress can not be empty" }) - } - - const tokenId = req.params.tokenId - if (!tokenId || tokenId === "") { - res.status(400) - return res.json({ error: "tokenId can not be empty" }) - } - - const burnAll = await SLP.TokenType1.burnAll({ - fundingAddress: fundingAddress, - fundingWif: fundingWif, - tokenId: tokenId, - bchChangeReceiverAddress: bchChangeReceiverAddress - }) - - res.status(200) - return res.json(burnAll) -} - -async function txDetails(req, res, next) { - try { - // Validate input parameter - const txid = req.params.txid - if (!txid || txid === "") { - res.status(400) - return res.json({ error: "txid can not be empty" }) - } - - if (txid.length !== 64) { - res.status(400) - return res.json({ error: "This is not a txid" }) - } - - let tmpSLP - if (process.env.NETWORK === "testnet") - tmpSLP = new SLPSDK({ restURL: process.env.TREST_URL }) - else tmpSLP = new SLPSDK({ restURL: process.env.REST_URL }) - - const tmpbitboxNetwork = new slp.BitboxNetwork(tmpSLP, slpValidator) - //console.log( - // `tmpbitboxNetwork: ${JSON.stringify(tmpbitboxNetwork, null, 2)}` - //) - - // Get TX info + token info - const result = await tmpbitboxNetwork.getTransactionDetails(txid) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) - - res.status(200) - return res.json(result) - } catch (err) { - wlogger.error(`Error in slp.ts/txDetails().`, err) - - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Handle corner case of mis-typted txid - if (err.error.indexOf("Not found") > -1) { - res.status(400) - return res.json({ error: "TXID not found" }) - } - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -async function tokenStats(req, res, next) { - const tokenId = req.params.tokenId - if (!tokenId || tokenId === "") { - res.status(400) - return res.json({ error: "tokenId can not be empty" }) - } - - try { - const query = { - v: 3, - q: { - db: ["t"], - find: { - $query: { - "tokenDetails.tokenIdHex": tokenId - } - }, - project: { tokenDetails: 1, tokenStats: 1, _id: 0 }, - limit: 10 - } - } - - const s = JSON.stringify(query) - const b64 = Buffer.from(s).toString("base64") - const url = `${process.env.SLPDB_URL}q/${b64}` - - // Get data from BitDB. - const tokenRes = await axios.get(url) - - const formattedTokens = [] - - if (tokenRes.data.t.length) { - tokenRes.data.t.forEach(token => { - token = formatTokenOutput(token) - formattedTokens.push(token.tokenDetails) - }) - } - - res.status(200) - return res.json(formattedTokens[0]) - } catch (err) { - wlogger.error(`Error in slp.ts/tokenStats().`, err) - - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - res.status(500) - return res.json({ error: `Error in /tokenStats: ${err.message}` }) - } -} - -// Retrieve transactions by tokenId and address. -async function txsTokenIdAddressSingle(req, res, next) { - try { - // Validate the input data. - const tokenId = req.params.tokenId - if (!tokenId || tokenId === "") { - res.status(400) - return res.json({ error: "tokenId can not be empty" }) - } - - const address = req.params.address - if (!address || address === "") { - res.status(400) - return res.json({ error: "address can not be empty" }) - } - - const query = { - v: 3, - q: { - find: { - db: ["c", "u"], - $query: { - $or: [ - { - "in.e.a": address - }, - { - "out.e.a": address - } - ], - "slp.detail.tokenIdHex": tokenId - }, - $orderby: { - "blk.i": -1 - } - }, - limit: 100 - }, - r: { - f: "[.[] | { txid: .tx.h, tokenDetails: .slp } ]" - } - } - - const s = JSON.stringify(query) - const b64 = Buffer.from(s).toString("base64") - const url = `${process.env.SLPDB_URL}q/${b64}` - - // Get data from SLPDB. - const tokenRes = await axios.get(url) - //console.log(`tokenRes.data: ${JSON.stringify(tokenRes.data,null,2)}`) - - return res.json(tokenRes.data.c) - } catch (err) { - wlogger.error(`Error in slp.ts/txsTokenIdAddressSingle().`, err) - - // Decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - res.status(500) - return res.json({ - error: `Error in /transactions/:tokenId/:address: ${err.message}` - }) - } -} - -module.exports = { - router, - testableComponents: { - root, - list, - listSingleToken, - listBulkToken, - balancesForAddress, - balancesForAddressByTokenID, - convertAddressSingle, - convertAddressBulk, - validateBulk, - isValidSlpTxid, - createTokenType1, - mintTokenType1, - sendTokenType1, - burnTokenType1, - burnAllTokenType1, - txDetails, - tokenStats, - balancesForTokenSingle, - txsTokenIdAddressSingle - } -} diff --git a/src/routes/v2/transaction.js b/src/routes/v2/transaction.js deleted file mode 100644 index 3f8e8eb..0000000 --- a/src/routes/v2/transaction.js +++ /dev/null @@ -1,188 +0,0 @@ -"use strict" - -const express = require("express") -const router = express.Router() -const axios = require("axios") - -const routeUtils = require("./route-utils") -const logger = require("./logging.js") -const wlogger = require("../../util/winston-logging") - -const BITBOXJS = require("@chris.troutner/bch-js") -const BITBOX = new BITBOXJS() - -// Used to convert error messages to strings, to safely pass to users. -const util = require("util") -util.inspect.defaultOptions = { depth: 3 } - -// Manipulates and formats the raw data comming from Insight API. -const processInputs = tx => { - // Add legacy and cashaddr to tx vin - if (tx.vin) { - tx.vin.forEach(vin => { - if (!vin.coinbase) { - vin.value = vin.valueSat - const address = vin.addr - if (address) { - vin.legacyAddress = BITBOX.Address.toLegacyAddress(address) - vin.cashAddress = BITBOX.Address.toCashAddress(address) - delete vin.addr - } - delete vin.valueSat - delete vin.doubleSpentTxID - } - }) - } - - // Add legacy and cashaddr to tx vout - if (tx.vout) { - tx.vout.forEach(vout => { - // Overwrite value string with value in satoshis - //vout.value = parseFloat(vout.value) * 100000000 - - if (vout.scriptPubKey) { - if (vout.scriptPubKey.addresses) { - const cashAddrs = [] - vout.scriptPubKey.addresses.forEach(addr => { - const cashAddr = BITBOX.Address.toCashAddress(addr) - cashAddrs.push(cashAddr) - }) - vout.scriptPubKey.cashAddrs = cashAddrs - } - } - }) - } -} - -router.get("/", root) -router.post("/details", detailsBulk) -router.get("/details/:txid", detailsSingle) - -function root(req, res, next) { - return res.json({ status: "transaction" }) -} - -// Retrieve transaction data from the Insight API -// This function is also used by the SLP route library. -async function transactionsFromInsight(txid) { - try { - const path = `${process.env.BITCOINCOM_BASEURL}tx/${txid}` - - // Query the Insight server. - const response = await axios.get(path) - //console.log(`Insight output: ${JSON.stringify(response.data, null, 2)}`) - - // Parse the data. - const parsed = response.data - if (parsed) processInputs(parsed) - - return parsed - } catch (err) { - // Dev Note: Do not log error messages here. Throw them instead and let the - // parent function handle it. - throw err - } -} - -async function detailsBulk(req, res, next) { - try { - const txids = req.body.txids - - // Reject if address is not an array. - if (!Array.isArray(txids)) { - res.status(400) - return res.json({ error: "txids needs to be an array" }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, txids)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - logger.debug(`Executing transaction/details with these txids: `, txids) - - // Collect an array of promises - const promises = txids.map( - async txid => await transactionsFromInsight(txid) - ) - - // Wait for all parallel promises to return. - const result = await Promise.all(promises) - - // Return the array of retrieved transaction information. - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - wlogger.error(`Error in transactions.ts/detailsBulk().`, err) - - //console.log(`Error in transaction details: `, err) - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// GET handler. Retrieve any unconfirmed TX information for a given address. -async function detailsSingle(req, res, next) { - try { - const txid = req.params.txid - if (!txid || txid === "") { - res.status(400) - return res.json({ error: "txid can not be empty" }) - } - - // Reject if address is an array. - if (Array.isArray(txid)) { - res.status(400) - return res.json({ - error: "txid can not be an array. Use POST for bulk upload." - }) - } - - logger.debug( - `Executing transaction.ts/detailsSingle with this txid: `, - txid - ) - - // Query the Insight API. - const retData = await transactionsFromInsight(txid) - //console.log(`retData: ${JSON.stringify(retData,null,2)}`) - - // Return the array of retrieved address information. - res.status(200) - return res.json(retData) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in transactions.ts/detailsSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -module.exports = { - router, - transactionsFromInsight, - testableComponents: { - root, - detailsBulk, - detailsSingle - } -} diff --git a/src/routes/v2/util.js b/src/routes/v2/util.js deleted file mode 100644 index fed94cc..0000000 --- a/src/routes/v2/util.js +++ /dev/null @@ -1,173 +0,0 @@ -"use strict" - -const express = require("express") -const router = express.Router() -const axios = require("axios") - -const routeUtils = require("./route-utils") -const logger = require("./logging.js") -const wlogger = require("../../util/winston-logging") - -const BITBOXJS = require("@chris.troutner/bch-js") -const BITBOX = new BITBOXJS() - -// Used to convert error messages to strings, to safely pass to users. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -const BitboxHTTP = axios.create({ - baseURL: process.env.RPC_BASEURL -}) - -const username = process.env.RPC_USERNAME -const password = process.env.RPC_PASSWORD - -const requestConfig = { - method: "post", - auth: { - username: username, - password: password - }, - data: { - jsonrpc: "1.0" - } -} - -router.get("/", root) -router.get("/validateAddress/:address", validateAddressSingle) -router.post("/validateAddress", validateAddressBulk) - -function root(req, res, next) { - return res.json({ status: "util" }) -} - -async function validateAddressSingle(req, res, next) { - try { - const address = req.params.address - if (!address || address === "") { - res.status(400) - return res.json({ error: "address can not be empty" }) - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "validateaddress" - requestConfig.data.method = "validateaddress" - requestConfig.data.params = [address] - - const response = await BitboxHTTP(requestConfig) - - return res.json(response.data.result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - wlogger.error(`Error in util.ts/validateAddressSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -async function validateAddressBulk(req, res, next) { - try { - const addresses = req.body.addresses - - // Reject if addresses is not an array. - if (!Array.isArray(addresses)) { - res.status(400) - return res.json({ - error: "addresses needs to be an array. Use GET for single address." - }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, addresses)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - // Validate each element in the array. - for (let i = 0; i < addresses.length; i++) { - const address = addresses[i] - - // Ensure the input is a valid BCH address. - try { - var legacyAddr = BITBOX.Address.toLegacyAddress(address) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${address}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(address) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - } - - logger.debug(`Executing util/validate with these addresses: `, addresses) - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - // Loop through each address and creates an array of requests to call in parallel - const promises = addresses.map(async address => { - requestConfig.data.id = "validateaddress" - requestConfig.data.method = "validateaddress" - requestConfig.data.params = [address] - - return await BitboxHTTP(requestConfig) - }) - - // Wait for all parallel Insight requests to return. - const axiosResult = await axios.all(promises) - - // Retrieve the data part of the result. - const result = axiosResult.map(x => x.data.result) - - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - wlogger.error(`Error in util.ts/validateAddressSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -module.exports = { - router, - testableComponents: { - root, - validateAddressSingle, - validateAddressBulk - } -} From 9b5827752eaa3965b61f31789cb030e464b6a252 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Thu, 31 Oct 2019 14:37:54 -0700 Subject: [PATCH 07/49] feat(full-node): moved full node routes to their own subdir --- package.json | 1 - src/app.js | 10 +- src/routes/v3/{ => full-node}/blockchain.js | 4 +- src/routes/v3/{ => full-node}/control.js | 4 +- src/routes/v3/{ => full-node}/mining.js | 4 +- src/routes/v3/{ => full-node}/network.js | 0 .../v3/{ => full-node}/rawtransactions.js | 4 +- test/v2/address.js | 1373 ----------------- test/v2/block.js | 610 -------- test/v2/blockchain.js | 1148 -------------- test/v2/control.js | 140 -- test/v2/e2e/address.js | 60 - test/v2/helpers/panda.js | 29 - test/v2/integration/README.md | 7 - test/v2/integration/rate-limits.js | 94 -- test/v2/integration/raw-transactions.js | 191 --- test/v2/mining.js | 176 --- test/v2/mocks/address-mock.js | 160 -- test/v2/mocks/block-mock.js | 42 - test/v2/mocks/blockchain-mock.js | 271 ---- test/v2/mocks/control-mock.js | 27 - test/v2/mocks/express-mocks.js | 88 -- test/v2/mocks/mining-mocks.js | 22 - test/v2/mocks/raw-transactions-mocks.js | 157 -- test/v2/mocks/slp-mocks.js | 327 ---- test/v2/mocks/slpjs-mocks.js | 71 - test/v2/mocks/transaction-mocks.js | 74 - test/v2/mocks/util-mocks.js | 18 - test/v2/rate-limits.js | 166 -- test/v2/raw-transactions.js | 807 ---------- test/v2/slp.js | 971 ------------ test/v2/transaction.js | 333 ---- test/v2/util.js | 300 ---- test/v3/blockchain.js | 2 +- test/v3/control.js | 2 +- test/v3/mining.js | 2 +- test/v3/rate-limits.js | 2 +- test/v3/raw-transactions.js | 2 +- 38 files changed, 18 insertions(+), 7681 deletions(-) rename src/routes/v3/{ => full-node}/blockchain.js (99%) rename src/routes/v3/{ => full-node}/control.js (96%) rename src/routes/v3/{ => full-node}/mining.js (97%) rename src/routes/v3/{ => full-node}/network.js (100%) rename src/routes/v3/{ => full-node}/rawtransactions.js (99%) delete mode 100644 test/v2/address.js delete mode 100644 test/v2/block.js delete mode 100644 test/v2/blockchain.js delete mode 100644 test/v2/control.js delete mode 100644 test/v2/e2e/address.js delete mode 100644 test/v2/helpers/panda.js delete mode 100644 test/v2/integration/README.md delete mode 100644 test/v2/integration/rate-limits.js delete mode 100644 test/v2/integration/raw-transactions.js delete mode 100644 test/v2/mining.js delete mode 100644 test/v2/mocks/address-mock.js delete mode 100644 test/v2/mocks/block-mock.js delete mode 100644 test/v2/mocks/blockchain-mock.js delete mode 100644 test/v2/mocks/control-mock.js delete mode 100644 test/v2/mocks/express-mocks.js delete mode 100644 test/v2/mocks/mining-mocks.js delete mode 100644 test/v2/mocks/raw-transactions-mocks.js delete mode 100644 test/v2/mocks/slp-mocks.js delete mode 100644 test/v2/mocks/slpjs-mocks.js delete mode 100644 test/v2/mocks/transaction-mocks.js delete mode 100644 test/v2/mocks/util-mocks.js delete mode 100644 test/v2/rate-limits.js delete mode 100644 test/v2/raw-transactions.js delete mode 100644 test/v2/slp.js delete mode 100644 test/v2/transaction.js delete mode 100644 test/v2/util.js diff --git a/package.json b/package.json index 36617f3..cd1070c 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ "start": "node ./src/app.js", "dev": "nodemon ./dist/app.js", "test": "npm run test-v3", - "test-v2": "export NETWORK=testnet && nyc --reporter=text mocha --timeout 25000 test/v2/", "test-v3": "export NETWORK=mainnet && nyc --reporter=text mocha --timeout 25000 test/v3/", "test:integration": "mocha test/v3/integration", "coverage": "nyc report --reporter=text-lcov | coveralls", diff --git a/src/app.js b/src/app.js index f9114df..99a8a29 100644 --- a/src/app.js +++ b/src/app.js @@ -22,11 +22,11 @@ const jwtAuth = require("./middleware/jwt-auth") // v3 const healthCheckV3 = require("./routes/v3/health-check") -const blockchainV3 = require("./routes/v3/blockchain") -const controlV3 = require("./routes/v3/control") -const miningV3 = require("./routes/v3/mining") -const networkV3 = require("./routes/v3/network") -const rawtransactionsV3 = require("./routes/v3/rawtransactions") +const blockchainV3 = require("./routes/v3/full-node/blockchain") +const controlV3 = require("./routes/v3/full-node/control") +const miningV3 = require("./routes/v3/full-node/mining") +const networkV3 = require("./routes/v3/full-node/network") +const rawtransactionsV3 = require("./routes/v3/full-node/rawtransactions") const utilV3 = require("./routes/v3/util") const slpV3 = require("./routes/v3/slp") const xpubV3 = require("./routes/v3/xpub") diff --git a/src/routes/v3/blockchain.js b/src/routes/v3/full-node/blockchain.js similarity index 99% rename from src/routes/v3/blockchain.js rename to src/routes/v3/full-node/blockchain.js index 092e8bf..8e2403b 100644 --- a/src/routes/v3/blockchain.js +++ b/src/routes/v3/full-node/blockchain.js @@ -9,8 +9,8 @@ const express = require("express") const router = express.Router() const axios = require("axios") -const routeUtils = require("./route-utils") -const wlogger = require("../../util/winston-logging") +const routeUtils = require("../route-utils") +const wlogger = require("../../../util/winston-logging") // Used to convert error messages to strings, to safely pass to users. const util = require("util") diff --git a/src/routes/v3/control.js b/src/routes/v3/full-node/control.js similarity index 96% rename from src/routes/v3/control.js rename to src/routes/v3/full-node/control.js index 6a3f6ee..e2e6374 100644 --- a/src/routes/v3/control.js +++ b/src/routes/v3/full-node/control.js @@ -4,8 +4,8 @@ const express = require("express") const router = express.Router() const axios = require("axios") -const routeUtils = require("./route-utils") -const wlogger = require("../../util/winston-logging") +const routeUtils = require("../route-utils") +const wlogger = require("../../../util/winston-logging") // Used for processing error messages before sending them to the user. const util = require("util") diff --git a/src/routes/v3/mining.js b/src/routes/v3/full-node/mining.js similarity index 97% rename from src/routes/v3/mining.js rename to src/routes/v3/full-node/mining.js index 60f48d6..daa6c10 100644 --- a/src/routes/v3/mining.js +++ b/src/routes/v3/full-node/mining.js @@ -4,8 +4,8 @@ const express = require("express") const router = express.Router() const axios = require("axios") -const routeUtils = require("./route-utils") -const wlogger = require("../../util/winston-logging") +const routeUtils = require("../route-utils") +const wlogger = require("../../../util/winston-logging") // Used to convert error messages to strings, to safely pass to users. const util = require("util") diff --git a/src/routes/v3/network.js b/src/routes/v3/full-node/network.js similarity index 100% rename from src/routes/v3/network.js rename to src/routes/v3/full-node/network.js diff --git a/src/routes/v3/rawtransactions.js b/src/routes/v3/full-node/rawtransactions.js similarity index 99% rename from src/routes/v3/rawtransactions.js rename to src/routes/v3/full-node/rawtransactions.js index d72beb9..7ae5998 100644 --- a/src/routes/v3/rawtransactions.js +++ b/src/routes/v3/full-node/rawtransactions.js @@ -4,8 +4,8 @@ const express = require("express") const router = express.Router() const axios = require("axios") -const routeUtils = require("./route-utils") -const wlogger = require("../../util/winston-logging") +const routeUtils = require("../route-utils") +const wlogger = require("../../../util/winston-logging") // Used to convert error messages to strings, to safely pass to users. const util = require("util") diff --git a/test/v2/address.js b/test/v2/address.js deleted file mode 100644 index 070c5d9..0000000 --- a/test/v2/address.js +++ /dev/null @@ -1,1373 +0,0 @@ -/* - TESTS FOR THE ADDRESS.JS LIBRARY - - This test file uses the environment variable TEST to switch between unit - and integration tests. By default, TEST is set to 'unit'. Set this variable - to 'integration' to run the tests against BCH mainnet. - - To-Do: - -/details/:address - --Verify to/from query options work correctly. - -GET /unconfirmed/:address & POST /unconfirmed - --Should initiate a transfer of BCH to verify unconfirmed TX. - ---This would be more of an e2e test. -*/ - -"use strict" - -const chai = require("chai") -const assert = chai.assert -const addressRoute = require("../../src/routes/v2/address") -const nock = require("nock") // HTTP mocking - -let originalUrl // Used during transition from integration to unit tests. - -// Mocking data. -const { mockReq, mockRes } = require("./mocks/express-mocks") -const mockData = require("./mocks/address-mock") - -// Used for debugging. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -describe("#AddressRouter", () => { - let req, res - - before(() => { - originalUrl = process.env.BITCOINCOM_BASEURL - - // Set default environment variables for unit tests. - if (!process.env.TEST) process.env.TEST = "unit" - if (process.env.TEST === "unit") - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - // console.log(`Testing type is: ${process.env.TEST}`) - }) - - // Setup the mocks before each test. - beforeEach(() => { - // Mock the req and res objects used by Express routes. - req = mockReq - res = mockRes - - // Explicitly reset the parmas and body. - req.params = {} - req.body = {} - req.query = {} - - // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() - }) - - afterEach(() => { - // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() - }) - - after(() => { - process.env.BITCOINCOM_BASEURL = originalUrl - }) - - describe("#root", () => { - // root route handler. - const root = addressRoute.testableComponents.root - - it("should respond to GET for base route", async () => { - const result = root(req, res) - - assert.equal(result.status, "address", "Returns static string") - }) - }) - - describe("#AddressDetailsBulk", () => { - // details route handler. - const detailsBulk = addressRoute.testableComponents.detailsBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single address", async () => { - req.body = { - address: `qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - } - - const result = await detailsBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid address", async () => { - req.body = { - addresses: [`02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - } - - const result = await detailsBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.addresses = testArray - - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should detect a network mismatch", async () => { - req.body = { - addresses: [`bitcoincash:qqqvv56zepke5k0xeaehlmjtmkv9ly2uzgkxpajdx3`] - } - - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await detailsBulk(req, res) - //console.log(`network issue result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.isAbove(res.statusCode, 499, "HTTP status code 500 expected.") - //assert.include(result.error, "ENOTFOUND", "Error message expected") - assert.include( - result.error, - "Network error: Could not communicate", - "Error message expected" - ) - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should default to page 0", async () => { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mgps7qxk2Z5ma4mXsviznnet8wx4VvMPFz?from=0&to=1000`) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert current page defaults to 0 - assert.equal(result[0].currentPage, 0) - }) - - it("should process the requested page", async () => { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`], - page: 5 - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mgps7qxk2Z5ma4mXsviznnet8wx4VvMPFz?from=5000&to=6000`) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert current page is same as requested - assert.equal(result[0].currentPage, 5) - }) - - it("should calculate the total number of pages", async () => { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mgps7qxk2Z5ma4mXsviznnet8wx4VvMPFz?from=0&to=1000`) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(result[0].pagesTotal, 1) - }) - - it("should get details for a single address", async () => { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mgps7qxk2Z5ma4mXsviznnet8wx4VvMPFz?from=0&to=1000`) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - // Assert that required fields exist in the returned object. - assert.equal(result.length, 1, "Array with one entry") - assert.hasAllKeys(result[0], [ - "balance", - "balanceSat", - "totalReceived", - "totalReceivedSat", - "totalSent", - "totalSentSat", - "unconfirmedBalance", - "unconfirmedBalanceSat", - "unconfirmedTxApperances", - "txApperances", - "transactions", - "legacyAddress", - "cashAddress", - "currentPage", - "pagesTotal" - ]) - }) - - it("should get details for multiple addresses", async () => { - req.body = { - addresses: [ - `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`, - `bchtest:qzknfggae0av6yvxk77gmyq7syc67yux6sk80haqyr` - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mgps7qxk2Z5ma4mXsviznnet8wx4VvMPFz?from=0&to=1000`) - .reply(200, mockData.mockAddressDetails) - - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mwJnEzXzKkveF2q5Af9jxi9j1zrtWAnPU8?from=0&to=1000`) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.equal(result.length, 2, "2 outputs for 2 inputs") - }) - }) - - describe("#AddressDetailsSingle", () => { - // details route handler. - const detailsSingle = addressRoute.testableComponents.detailsSingle - - it("should throw 400 if address is empty", async () => { - const result = await detailsSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "address can not be empty") - }) - - it("should error on an array", async () => { - req.params.address = [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - - const result = await detailsSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "address can not be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid address", async () => { - req.params.address = `02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - const result = await detailsSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should detect a network mismatch", async () => { - req.params.address = `bitcoincash:qqqvv56zepke5k0xeaehlmjtmkv9ly2uzgkxpajdx3` - - const result = await detailsSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.params.address = `qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await detailsSingle(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should default to page 0", async () => { - req.params.address = `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mgps7qxk2Z5ma4mXsviznnet8wx4VvMPFz?from=0&to=1000`) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert current page defaults to 0 - assert.equal(result.currentPage, 0) - }) - - it("should process the requested page", async () => { - req.params.address = `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - req.query.page = 5 - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mgps7qxk2Z5ma4mXsviznnet8wx4VvMPFz?from=5000&to=6000`) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsSingle(req, res) - - // Assert current page is same as requested - assert.equal(result.currentPage, 5) - }) - - it("should calculate the total number of pages", async () => { - req.params.address = `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mgps7qxk2Z5ma4mXsviznnet8wx4VvMPFz?from=0&to=1000`) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsSingle(req, res) - - assert.equal(result.pagesTotal, 1) - }) - - it("should get details for a single address", async () => { - req.params.address = `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mgps7qxk2Z5ma4mXsviznnet8wx4VvMPFz?from=0&to=1000`) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert that required fields exist in the returned object. - assert.hasAllKeys(result, [ - "balance", - "balanceSat", - "totalReceived", - "totalReceivedSat", - "totalSent", - "totalSentSat", - "unconfirmedBalance", - "unconfirmedBalanceSat", - "unconfirmedTxApperances", - "txApperances", - "transactions", - "legacyAddress", - "cashAddress", - "currentPage", - "pagesTotal" - ]) - }) - }) - - describe("#AddressUtxoBulk", () => { - // utxo route handler. - const utxoBulk = addressRoute.testableComponents.utxoBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await utxoBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single address", async () => { - req.body = { - address: `qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - } - - const result = await utxoBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid address", async () => { - req.body = { - addresses: [`02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - } - - const result = await utxoBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should detect a network mismatch", async () => { - req.body = { - addresses: [`bitcoincash:qqqvv56zepke5k0xeaehlmjtmkv9ly2uzgkxpajdx3`] - } - - const result = await utxoBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.body = { - addresses: [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - } - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api" - - const result = await utxoBulk(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get utxos for a single address", async () => { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mgps7qxk2Z5ma4mXsviznnet8wx4VvMPFz/utxo`) - .reply(200, mockData.mockUtxoDetails) - } - - // Call the details API. - const result = await utxoBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result, "result should be an array") - - // Each element should have these primary properties. - assert.hasAllKeys(result[0], [ - "utxos", - "legacyAddress", - "cashAddress", - "scriptPubKey" - ]) - - // Validate the UTXO data structure. - assert.hasAnyKeys(result[0].utxos[0], [ - "txid", - "vout", - "amount", - "satoshis", - "height", - "confirmations" - ]) - }) - - it("should get utxos for mulitple addresses", async () => { - req.body = { - addresses: [ - `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`, - `bchtest:qzknfggae0av6yvxk77gmyq7syc67yux6sk80haqyr` - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mgps7qxk2Z5ma4mXsviznnet8wx4VvMPFz/utxo`) - .reply(200, mockData.mockUtxoDetails) - - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mwJnEzXzKkveF2q5Af9jxi9j1zrtWAnPU8/utxo`) - .reply(200, mockData.mockUtxoDetails) - } - - // Call the details API. - const result = await utxoBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.equal(result.length, 2, "2 outputs for 2 inputs") - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.addresses = testArray - - const result = await utxoBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - }) - - describe("#AddressUtxoSingle", () => { - // details route handler. - const utxoSingle = addressRoute.testableComponents.utxoSingle - - it("should throw 400 if address is empty", async () => { - const result = await utxoSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "address can not be empty") - }) - - it("should error on an array", async () => { - req.params.address = [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - - const result = await utxoSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "address can not be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid address", async () => { - req.params.address = `02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - const result = await utxoSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should detect a network mismatch", async () => { - req.params.address = `bitcoincash:qqqvv56zepke5k0xeaehlmjtmkv9ly2uzgkxpajdx3` - - const result = await utxoSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.params.address = `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await utxoSingle(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get details for a single address", async () => { - req.params.address = `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mgps7qxk2Z5ma4mXsviznnet8wx4VvMPFz/utxo`) - .reply(200, mockData.mockUtxoDetails) - } - - // Call the details API. - const result = await utxoSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Each element should have these primary properties. - assert.hasAllKeys(result, [ - "utxos", - "legacyAddress", - "cashAddress", - "scriptPubKey" - ]) - - // Validate the UTXO data structure. - assert.hasAnyKeys(result.utxos[0], [ - "txid", - "vout", - "amount", - "satoshis", - "height", - "confirmations" - ]) - }) - }) - - describe("#AddressUnconfirmedBulk", () => { - // unconfirmed route handler. - const unconfirmedBulk = addressRoute.testableComponents.unconfirmedBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await unconfirmedBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single address", async () => { - req.body = { - address: `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - } - - const result = await unconfirmedBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.addresses = testArray - - const result = await unconfirmedBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should throw an error for an invalid address", async () => { - req.body = { - addresses: [`02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - } - - const result = await unconfirmedBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should detect a network mismatch", async () => { - req.body = { - addresses: [`bitcoincash:qqqvv56zepke5k0xeaehlmjtmkv9ly2uzgkxpajdx3`] - } - - const result = await unconfirmedBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.body = { - addresses: [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - } - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api" - - const result = await unconfirmedBulk(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get unconfirmed data for a single address", async () => { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mgps7qxk2Z5ma4mXsviznnet8wx4VvMPFz/utxo`) - .reply(200, mockData.mockUtxoDetails) - } - - // Call the details API. - const result = await unconfirmedBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - //console.log(`result[0].utxos: ${util.inspect(result[0].utxos)}`) - - assert.isArray(result, "result should be an array") - - // Dev note: Unconfirmed TXs are hard to test in an integration test because - // the nature of an unconfirmed transation is transient. It quickly becomes - // confirmed and thus should not show up. - }) - - it("should get unconfirmed data for an array of addresses", async () => { - req.body = { - addresses: [ - `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`, - `bchtest:qzknfggae0av6yvxk77gmyq7syc67yux6sk80haqyr` - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mgps7qxk2Z5ma4mXsviznnet8wx4VvMPFz/utxo`) - .reply(200, mockData.mockUtxoDetails) - - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mwJnEzXzKkveF2q5Af9jxi9j1zrtWAnPU8/utxo`) - .reply(200, mockData.mockUtxoDetails) - } - - // Call the details API. - const result = await unconfirmedBulk(req, res) - - assert.isArray(result) - }) - }) - - describe("#AddressUnconfirmedSingle", () => { - // details route handler. - const unconfirmedSingle = addressRoute.testableComponents.unconfirmedSingle - - it("should throw 400 if address is empty", async () => { - const result = await unconfirmedSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "address can not be empty") - }) - - it("should error on an array", async () => { - req.params.address = [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - - const result = await unconfirmedSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "address can not be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid address", async () => { - req.params.address = `02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - const result = await unconfirmedSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should detect a network mismatch", async () => { - req.params.address = `bitcoincash:qqqvv56zepke5k0xeaehlmjtmkv9ly2uzgkxpajdx3` - - const result = await unconfirmedSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.params.address = `qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await unconfirmedSingle(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get details for a single address", async () => { - req.params.address = `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/addr/mgps7qxk2Z5ma4mXsviznnet8wx4VvMPFz/utxo`) - .reply(200, mockData.mockUtxoDetails) - } - - // Call the details API. - const result = await unconfirmedSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Each element should have these primary properties. - assert.hasAllKeys(result, [ - "utxos", - "legacyAddress", - "cashAddress", - "scriptPubKey" - ]) - - assert.isArray(result.utxos) - }) - }) - - describe("#AddressTransactionsBulk", () => { - // unconfirmed route handler. - const transactionsBulk = addressRoute.testableComponents.transactionsBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await transactionsBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single address", async () => { - req.body = { - address: `qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - } - - const result = await transactionsBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.addresses = testArray - - const result = await transactionsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should throw an error for an invalid address", async () => { - req.body = { - addresses: [`02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - } - - const result = await transactionsBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should detect a network mismatch", async () => { - req.body = { - addresses: [`bitcoincash:qqqvv56zepke5k0xeaehlmjtmkv9ly2uzgkxpajdx3`] - } - - const result = await transactionsBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.body = { - addresses: [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - } - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api" - - const result = await transactionsBulk(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should default to page 0", async () => { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get( - `/txs/?address=bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4&pageNum=0` - ) - .reply(200, mockData.mockTransactions) - } - - // Call the endpoint - const result = await transactionsBulk(req, res) - - // Assert current page defaults to 0 - assert.equal(result[0].currentPage, 0) - }) - - it("should process the requested page", async () => { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`], - page: 5 - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get( - `/txs/?address=bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4&pageNum=5` - ) - .reply(200, mockData.mockTransactions) - } - - // Call the endpoint - const result = await transactionsBulk(req, res) - - // Assert current page is same as requested - assert.equal(result[0].currentPage, 5) - }) - - it("should get transactions for a single address", async () => { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get( - `/txs/?address=bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4&pageNum=0` - ) - .reply(200, mockData.mockTransactions) - } - - // Call the details API. - const result = await transactionsBulk(req, res) - - assert.isArray(result, "result should be an array") - - assert.exists(result[0].pagesTotal) - assert.exists(result[0].currentPage) - assert.exists(result[0].txs) - assert.isArray(result[0].txs) - assert.exists(result[0].legacyAddress) - assert.exists(result[0].cashAddress) - }) - - it("should get transactions for an array of addresses", async () => { - req.body = { - addresses: [ - `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`, - `bchtest:qzknfggae0av6yvxk77gmyq7syc67yux6sk80haqyr` - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get( - `/txs/?address=bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4&pageNum=0` - ) - .reply(200, mockData.mockTransactions) - - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get( - `/txs/?address=bchtest:qzknfggae0av6yvxk77gmyq7syc67yux6sk80haqyr&pageNum=0` - ) - .reply(200, mockData.mockTransactions) - } - - // Call the details API. - const result = await transactionsBulk(req, res) - - assert.isArray(result, "result should be an array") - - assert.equal(result.length, 2, "Array should have 2 elements") - }) - }) - - describe("#AddressTransactionsSingle", () => { - // details route handler. - const transactionsSingle = - addressRoute.testableComponents.transactionsSingle - - it("should throw 400 if address is empty", async () => { - const result = await transactionsSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "address can not be empty") - }) - - it("should error on an array", async () => { - req.params.address = [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - - const result = await transactionsSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "address can not be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid address", async () => { - req.params.address = `02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - const result = await transactionsSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should detect a network mismatch", async () => { - req.params.address = `bitcoincash:qqqvv56zepke5k0xeaehlmjtmkv9ly2uzgkxpajdx3` - - const result = await transactionsSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.params.address = `qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await transactionsSingle(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should default to page 0", async () => { - req.params.address = `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get( - `/txs/?address=bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4&pageNum=0` - ) - .reply(200, mockData.mockTransactions) - } - - // Call the endpoint - const result = await transactionsSingle(req, res) - - // Assert current page defaults to 0 - assert.equal(result.currentPage, 0) - }) - - it("should process the requested page", async () => { - req.params.address = `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - req.query.page = 5 - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get( - `/txs/?address=bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4&pageNum=5` - ) - .reply(200, mockData.mockTransactions) - } - - // Call the endpoint - const result = await transactionsSingle(req, res) - - // Assert current page is same as requested - assert.equal(result.currentPage, 5) - }) - - it("should get details for a single address", async () => { - req.params.address = `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get( - `/txs/?address=bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4&pageNum=0` - ) - .reply(200, mockData.mockTransactions) - } - - // Call the details API. - const result = await transactionsSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert that required fields exist in the returned object. - assert.exists(result.pagesTotal) - assert.exists(result.currentPage) - assert.exists(result.txs) - assert.isArray(result.txs) - assert.exists(result.legacyAddress) - assert.exists(result.cashAddress) - }) - }) - - describe("#AddressFromXPubSingle", () => { - // details route handler. - const fromXPubSingle = addressRoute.testableComponents.fromXPubSingle - - it("should throw 400 if xpub is empty", async () => { - const result = await fromXPubSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "xpub can not be empty") - }) - - it("should error on an array", async () => { - req.params.xpub = [ - `tpubDHTK2jqg73w3GwoiHfAMbMYML1HN8FhrUxD9rFgbSgHXdwwrY6pAFqKDfUHhqw7vreaZty5hPGjb1S7ZPQeMmu6TFHAKfY9tJpYbvaGjPRM` - ] - - const result = await fromXPubSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "xpub can not be an array", - "Proper error message" - ) - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.params.xpub = `tpubDHTK2jqg73w3GwoiHfAMbMYML1HN8FhrUxD9rFgbSgHXdwwrY6pAFqKDfUHhqw7vreaZty5hPGjb1S7ZPQeMmu6TFHAKfY9tJpYbvaGjPRM` - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await fromXPubSingle(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should create an address from xpub", async () => { - req.params.xpub = `tpubDHTK2jqg73w3GwoiHfAMbMYML1HN8FhrUxD9rFgbSgHXdwwrY6pAFqKDfUHhqw7vreaZty5hPGjb1S7ZPQeMmu6TFHAKfY9tJpYbvaGjPRM` - - // Mock the Insight URL for unit tests. - // TODO add unit test - // if (process.env.TEST === "unit") { - // nock(`${process.env.BITCOINCOM_BASEURL}`) - // .get( - // `/txs/?address=bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4&pageNum=0` - // ) - // .reply(200, mockData.mockTransactions) - // } - - // Call the details API. - const result = await fromXPubSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert that required fields exist in the returned object. - assert.exists(result.legacyAddress) - assert.exists(result.cashAddress) - }) - }) -}) diff --git a/test/v2/block.js b/test/v2/block.js deleted file mode 100644 index 3817e62..0000000 --- a/test/v2/block.js +++ /dev/null @@ -1,610 +0,0 @@ -"use strict" - -const blockRoute = require("../../src/routes/v2/block") -const chai = require("chai") -const assert = chai.assert -const nock = require("nock") // HTTP mocking - -let originalEnvVars // Used during transition from integration to unit tests. - -// Mocking data. -const { mockReq, mockRes } = require("./mocks/express-mocks") -const mockData = require("./mocks/block-mock") - -// Used for debugging. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -describe("#Block", () => { - let req, res - - before(() => { - // Save existing environment variables. - originalEnvVars = { - BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL, - RPC_BASEURL: process.env.RPC_BASEURL, - RPC_USERNAME: process.env.RPC_USERNAME, - RPC_PASSWORD: process.env.RPC_PASSWORD - } - - // Set default environment variables for unit tests. - if (!process.env.TEST) process.env.TEST = "unit" - if (process.env.TEST === "unit") { - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - process.env.RPC_BASEURL = "http://fakeurl/api" - process.env.RPC_USERNAME = "fakeusername" - process.env.RPC_PASSWORD = "fakepassword" - } - }) - - // Setup the mocks before each test. - beforeEach(() => { - // Mock the req and res objects used by Express routes. - req = mockReq - res = mockRes - - // Explicitly reset the parmas and body. - req.params = {} - req.body = {} - - // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() - }) - - afterEach(() => { - // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() - }) - - after(() => { - // Restore any pre-existing environment variables. - process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL - process.env.RPC_BASEURL = originalEnvVars.RPC_BASEURL - process.env.RPC_USERNAME = originalEnvVars.RPC_USERNAME - process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD - }) - - describe("#root", () => { - // root route handler. - const root = blockRoute.testableComponents.root - - it("should respond to GET for base route", async () => { - const result = root(req, res) - - assert.equal(result.status, "block", "Returns static string") - }) - }) - - describe("#detailsByHashSingle", () => { - const detailsByHash = blockRoute.testableComponents.detailsByHashSingle - - it("should throw an error for an empty hash", async () => { - req.params.hash = "" - - const result = await detailsByHash(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "hash must not be empty", - "Proper error message" - ) - }) - - it("should throw 50X when network issues", async () => { - // Save the existing RPC URL. - const savedUrl = process.env.BITCOINCOM_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - req.params.hash = "abc123" - const result = await detailsByHash(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.isAbove(res.statusCode, 499, "HTTP status code 50X expected.") - //assert.include(result.error, "ENOTFOUND", "Error message expected") - }) - - it("should throw an error for invalid hash", async () => { - req.params.hash = "abc123" - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/block/${req.params.hash}`) - .reply(404, "Not found") - } - - const result = await detailsByHash(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.include(result.error, "Not found", "Proper error message") - }) - - it("should GET /detailsByHash/:hash", async () => { - req.params.hash = - "00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79" - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/block/${req.params.hash}`) - .reply(200, mockData.mockBlockDetails) - } - - const result = await detailsByHash(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAnyKeys(result, [ - "hash", - "size", - "height", - "version", - "merkleroot", - "tx", - "time", - "nonce", - "bits", - "difficulty", - "chainwork", - "confirmations", - "previousblockhash", - "nextblockhash", - "reward", - "isMainChain", - "poolInfo" - ]) - assert.isArray(result.tx) - }) - }) - - describe("#detailsByHashBulk", () => { - // details route handler. - const detailsByHashBulk = blockRoute.testableComponents.detailsByHashBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await detailsByHashBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "hashes needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single address", async () => { - req.body = { - hashes: - "00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79" - } - - const result = await detailsByHashBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "hashes needs to be an array", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.hashes = testArray - - const result = await detailsByHashBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should throw an error for an invalid hash", async () => { - req.body = { - hashes: [`abc123`] - } - - const result = await detailsByHashBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid hash", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.body = { - hashes: [ - "00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79" - ] - } - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await detailsByHashBulk(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get details for a single hash", async () => { - req.body = { - hashes: [ - "00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79" - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/block/${req.body.hashes[0]}`) - .reply(200, mockData.mockBlockDetails) - } - - // Call the details API. - const result = await detailsByHashBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert that required fields exist in the returned object. - assert.equal(result.length, 1, "Array with one entry") - assert.hasAllKeys(result[0], [ - "bits", - "chainwork", - "confirmations", - "difficulty", - "hash", - "height", - "isMainChain", - "merkleroot", - "nextblockhash", - "nonce", - "poolInfo", - "previousblockhash", - "reward", - "size", - "time", - "tx", - "version" - ]) - }) - - it("should get details for multiple hashes", async () => { - req.body = { - hashes: [ - `00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79`, - `00000000c2b2c19cf499f57d5b0f724c6df753330d7acc7d4a8ebe412d427bd0` - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/block/${req.body.hashes[0]}`) - .reply(200, mockData.mockBlockDetails) - - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/block/${req.body.hashes[1]}`) - .reply(200, mockData.mockBlockDetails) - } - - // Call the details API. - const result = await detailsByHashBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.equal(result.length, 2, "2 outputs for 2 inputs") - }) - - it("should throw an error if hash not found", async () => { - req.body = { - hashes: [ - `00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44abcdef` - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/block/${req.body.hashes[0]}`) - //.reply(404, { error: { message: "Not Found" } }) - .reply(404, "Not found") - } - - const result = await detailsByHashBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 404, "HTTP status code 404 expected.") - assert.include(result.error, "Not found", "Proper error message") - }) - }) - - describe("Block Details By Height", () => { - // block route handler. - const detailsByHeight = blockRoute.testableComponents.detailsByHeightSingle - - it("should throw an error for an empty height", async () => { - req.params.height = "" - - const result = await detailsByHeight(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "height must not be empty", - "Proper error message" - ) - }) - - it("should throw 500 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl = process.env.BITCOINCOM_BASEURL - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - process.env.RPC_BASEURL = "http://fakeurl/api/" - - req.params.height = "abc123" - const result = await detailsByHeight(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - process.env.RPC_BASEURL = savedUrl2 - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or great expected." - ) - //assert.include(result.error, "ENOTFOUND", "Error message expected") - }) - - it("should throw an error for invalid height", async () => { - req.params.height = "abc123" - - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(500, { - error: { - code: -1, - message: "JSON value is not an integer as expected" - } - }) - } - - const result = await detailsByHeight(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "JSON value is not an integer as expected", - "Proper error message" - ) - }) - - it("should GET /detailsByHeight/:height", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockBlockHash }) - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get( - `/block/00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79` - ) - .reply(200, mockData.mockBlockDetails) - } - - req.params.height = 500000 - - const result = await detailsByHeight(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAnyKeys(result, [ - "hash", - "size", - "height", - "version", - "merkleroot", - "tx", - "time", - "nonce", - "bits", - "difficulty", - "chainwork", - "confirmations", - "previousblockhash", - "nextblockhash", - "reward", - "isMainChain", - "poolInfo" - ]) - assert.isArray(result.tx) - }) - }) - - describe("#detailsByHeightBulk", () => { - // details route handler. - const detailsByHeightBulk = - blockRoute.testableComponents.detailsByHeightBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await detailsByHeightBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "heights needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single height", async () => { - req.body = { - heights: 500000 - } - - const result = await detailsByHeightBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "heights needs to be an array", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.heights = testArray - - const result = await detailsByHeightBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should throw error for an invalid height", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(500, { - error: { - code: -1, - message: "JSON value is not an integer as expected" - } - }) - } - - req.body.heights = [`abc123`] - - const result = await detailsByHeightBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.body.heights = [`500000`] - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await detailsByHeightBulk(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get details for a single height", async () => { - req.body.heights = [`500000`] - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockBlockHash }) - - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/block/${mockData.mockBlockHash}`) - .reply(200, mockData.mockBlockDetails) - } - - // Call the details API. - const result = await detailsByHeightBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert that required fields exist in the returned object. - assert.equal(result.length, 1, "Array with one entry") - assert.hasAllKeys(result[0], [ - "bits", - "chainwork", - "confirmations", - "difficulty", - "hash", - "height", - "isMainChain", - "merkleroot", - "nextblockhash", - "nonce", - "poolInfo", - "previousblockhash", - "reward", - "size", - "time", - "tx", - "version" - ]) - }) - - it("should get details for multiple block heights", async () => { - req.body = { - heights: [`500000`, `500001`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .times(2) - .reply(200, { result: mockData.mockBlockHash }) - - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/block/${mockData.mockBlockHash}`) - .times(2) - .reply(200, mockData.mockBlockDetails) - } - - // Call the details API. - const result = await detailsByHeightBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.equal(result.length, 2, "2 outputs for 2 inputs") - }) - }) -}) diff --git a/test/v2/blockchain.js b/test/v2/blockchain.js deleted file mode 100644 index a2b4458..0000000 --- a/test/v2/blockchain.js +++ /dev/null @@ -1,1148 +0,0 @@ -/* - TODO: - -getRawMempool - --Add tests for 'verbose' input values - -getMempoolEntry & getMempoolEntryBulk - --Needs e2e test to create unconfirmed tx, for real-world test. -*/ - -"use strict" - -const chai = require("chai") -const assert = chai.assert -const nock = require("nock") // HTTP mocking -const blockchainRoute = require("../../src/routes/v2/blockchain") - -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -if (!process.env.TEST) process.env.TEST = "unit" - -// Mocking data. -const { mockReq, mockRes } = require("./mocks/express-mocks") -const mockData = require("./mocks/blockchain-mock") - -let originalEnvVars // Used during transition from integration to unit tests. - -describe("#BlockchainRouter", () => { - let req, res - - // local node will be started in regtest mode on the port 48332 - //before(panda.runLocalNode) - - before(() => { - // Save existing environment variables. - originalEnvVars = { - BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL, - RPC_BASEURL: process.env.RPC_BASEURL, - RPC_USERNAME: process.env.RPC_USERNAME, - RPC_PASSWORD: process.env.RPC_PASSWORD - } - - // Set default environment variables for unit tests. - if (process.env.TEST === "unit") { - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - process.env.RPC_BASEURL = "http://fakeurl/api" - process.env.RPC_USERNAME = "fakeusername" - process.env.RPC_PASSWORD = "fakepassword" - } - }) - - // Setup the mocks before each test. - beforeEach(() => { - // Mock the req and res objects used by Express routes. - req = mockReq - res = mockRes - - // Explicitly reset the parmas and body. - req.params = {} - req.body = {} - - // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() - }) - - afterEach(() => { - // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() - }) - - after(() => { - // otherwise the panda will run forever - //process.exit() - - // Restore any pre-existing environment variables. - process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL - process.env.RPC_BASEURL = originalEnvVars.RPC_BASEURL - process.env.RPC_USERNAME = originalEnvVars.RPC_USERNAME - process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD - }) - - describe("#root", () => { - // root route handler. - const root = blockchainRoute.testableComponents.root - - it("should respond to GET for base route", async () => { - const result = root(req, res) - - assert.equal(result.status, "blockchain", "Returns static string") - }) - }) - - describe("getBestBlockHash()", () => { - // block route handler. - const getBestBlockHash = blockchainRoute.testableComponents.getBestBlockHash - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - const result = await getBestBlockHash(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") - assert.include( - result.error, - "Could not communicate with full node", - "Error message expected" - ) - }) - - it("should GET /getBestBlockHash", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockBlockHash }) - } - - const result = await getBestBlockHash(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isString(result) - assert.equal(result.length, 64, "Hash string is fixed length") - }) - }) - - describe("getBlockchainInfo()", () => { - // block route handler. - const getBlockchainInfo = - blockchainRoute.testableComponents.getBlockchainInfo - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - const result = await getBlockchainInfo(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") - assert.include( - result.error, - "Could not communicate with full node", - "Error message expected" - ) - }) - - it("should GET /getBlockchainInfo", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockBlockchainInfo }) - } - - const result = await getBlockchainInfo(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAnyKeys(result, [ - "chain", - "blocks", - "headers", - "bestblockhash", - "difficulty", - "mediantime", - "verificationprogress", - "chainwork", - "pruned", - "softforks", - "bip9_softforks" - ]) - }) - }) - - describe("getBlockCount()", () => { - // block route handler. - const getBlockCount = blockchainRoute.testableComponents.getBlockCount - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - const result = await getBlockCount(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") - assert.include( - result.error, - "Could not communicate with full node", - "Error message expected" - ) - }) - - it("should GET /getBlockCount", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: 126769 }) - } - - const result = await getBlockCount(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isNumber(result) - }) - }) - - describe("getBlockHeaderSingle()", async () => { - const getBlockHeader = - blockchainRoute.testableComponents.getBlockHeaderSingle - - it("should throw 400 error if hash is missing", async () => { - const result = await getBlockHeader(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "hash can not be empty") - }) - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - req.params.hash = - "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900" - - const result = await getBlockHeader(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") - assert.include( - result.error, - "Network error: Could not communicate with full node", - "Error message expected" - ) - }) - - it("should GET block header", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { - result: - "0000ff7f7d217c9b7845ea8b50d620c59a1bf7c276566406e9b7bc7e463e0000000000006d70322c0b697c1c81d2744f87f09f1e9780ba5d30338952e2cdc64e60456f8423bb0a5ceafa091a3e843526" - }) - } - - req.params.hash = - "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900" - - const result = await getBlockHeader(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isString(result) - assert.equal( - result, - "0000ff7f7d217c9b7845ea8b50d620c59a1bf7c276566406e9b7bc7e463e0000000000006d70322c0b697c1c81d2744f87f09f1e9780ba5d30338952e2cdc64e60456f8423bb0a5ceafa091a3e843526" - ) - }) - - it("should GET verbose block header", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockBlockHeader }) - } - - req.query.verbose = true - req.params.hash = - "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900" - - const result = await getBlockHeader(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, [ - "hash", - "confirmations", - "height", - "version", - "versionHex", - "merkleroot", - "time", - "mediantime", - "nonce", - "bits", - "difficulty", - "chainwork", - "previousblockhash", - "nextblockhash" - ]) - }) - }) - - describe("#getBlockHeaderBulk", () => { - // route handler. - const getBlockHeaderBulk = - blockchainRoute.testableComponents.getBlockHeaderBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await getBlockHeaderBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "hashes needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single hash", async () => { - req.body.hashes = - "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900" - - const result = await getBlockHeaderBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "hashes needs to be an array", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.hashes = testArray - - const result = await getBlockHeaderBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should throw a 400 error for an invalid hash", async () => { - req.body.hashes = ["badHash"] - - await getBlockHeaderBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.body.hashes = [ - "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900" - ] - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await getBlockHeaderBulk(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get concise block header for a single hash", async () => { - req.body.hashes = [ - "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900" - ] - - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockBlockHeaderConcise }) - } - - // Call the details API. - const result = await getBlockHeaderBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert that required fields exist in the returned object. - assert.isArray(result) - assert.equal( - result[0], - "0000ff7f7d217c9b7845ea8b50d620c59a1bf7c276566406e9b7bc7e463e0000000000006d70322c0b697c1c81d2744f87f09f1e9780ba5d30338952e2cdc64e60456f8423bb0a5ceafa091a3e843526" - ) - }) - - it("should get verbose block header for a single hash", async () => { - req.body = { - hashes: [ - "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900" - ], - verbose: true - } - - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockBlockHeader }) - } - - // Call the details API. - const result = await getBlockHeaderBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert that required fields exist in the returned object. - assert.isArray(result) - assert.hasAllKeys(result[0], [ - "hash", - "confirmations", - "height", - "version", - "versionHex", - "merkleroot", - "time", - "mediantime", - "nonce", - "bits", - "difficulty", - "chainwork", - "previousblockhash", - "nextblockhash" - ]) - }) - - it("should get details for multiple block heights", async () => { - req.body.hashes = [ - "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900", - "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900" - ] - - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .times(2) - .reply(200, { result: mockData.mockBlockHeaderConcise }) - } - - // Call the details API. - const result = await getBlockHeaderBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.equal(result.length, 2, "2 outputs for 2 inputs") - }) - }) - - describe("getChainTips()", () => { - // block route handler. - const getChainTips = blockchainRoute.testableComponents.getChainTips - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - const result = await getChainTips(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") - assert.include( - result.error, - "Could not communicate with full node", - "Error message expected" - ) - }) - - it("should GET /getChainTips", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockChainTips }) - } - - const result = await getChainTips(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAnyKeys(result[0], ["height", "hash", "branchlen", "status"]) - }) - }) - - describe("getDifficulty()", () => { - // block route handler. - const getDifficulty = blockchainRoute.testableComponents.getDifficulty - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - const result = await getDifficulty(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") - assert.include( - result.error, - "Could not communicate with full node", - "Error message expected" - ) - }) - - it("should GET /getDifficulty", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: 4049809.205246544 }) - } - - const result = await getDifficulty(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isNumber(result) - }) - }) - - describe("getMempoolInfo()", () => { - // block route handler. - const getMempoolInfo = blockchainRoute.testableComponents.getMempoolInfo - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - const result = await getMempoolInfo(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") - assert.include( - result.error, - "Could not communicate with full node", - "Error message expected" - ) - }) - - it("should GET /getMempoolInfo", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockMempoolInfo }) - } - - const result = await getMempoolInfo(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAnyKeys(result, [ - "result", - "bytes", - "usage", - "maxmempool", - "mempoolminfree" - ]) - }) - }) - - describe("getRawMempool()", () => { - // block route handler. - const getRawMempool = blockchainRoute.testableComponents.getRawMempool - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - const result = await getRawMempool(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") - assert.include( - result.error, - "Could not communicate with full node", - "Error message expected" - ) - }) - - it("should GET /getMempoolInfo", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockRawMempool }) - } - - const result = await getRawMempool(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - // Not sure what other assertions should be made here. - }) - }) - - describe("getMempoolEntry()", () => { - // block route handler. - const getMempoolEntry = - blockchainRoute.testableComponents.getMempoolEntrySingle - - it("should throw 400 if txid is empty", async () => { - const result = await getMempoolEntry(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "txid can not be empty") - }) - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - - const result = await getMempoolEntry(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") - assert.include( - result.error, - "Could not communicate with full node", - "Error message expected" - ) - }) - - it("should GET /getMempoolEntry", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: { error: "Transaction not in mempool" } }) - } - - req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - - const result = await getMempoolEntry(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.isString(result.error) - assert.equal(result.error, "Transaction not in mempool") - }) - }) - - describe("#getMempoolEntryBulk", () => { - // route handler. - const getMempoolEntryBulk = - blockchainRoute.testableComponents.getMempoolEntryBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await getMempoolEntryBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "txids needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single txid", async () => { - req.body.txids = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - - const result = await getMempoolEntryBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "txids needs to be an array", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.txids = testArray - - const result = await getMempoolEntryBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - // Only execute on integration tests. - if (process.env.TEST !== "unit") { - // Dev-note: This test passes because it expects an error. TXIDs do not - // stay in the mempool for long, so it does not work well for a unit or - // integration test. - it("should retrieve single mempool entry", async () => { - req.body.txids = [ - `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - ] - - const result = await getMempoolEntryBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.isString(result.error) - assert.equal(result.error, "Transaction not in mempool") - }) - - // Dev-note: This test passes because it expects an error. TXIDs do not - // stay in the mempool for long, so it does not work well for a unit or - // integration test. - it("should retrieve multiple mempool entries", async () => { - req.body.txids = [ - `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde`, - `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - ] - - const result = await getMempoolEntryBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.isString(result.error) - assert.equal(result.error, "Transaction not in mempool") - }) - } - }) - - describe("getTxOut()", () => { - // block route handler. - const getTxOut = blockchainRoute.testableComponents.getTxOut - - it("should throw 400 if txid is empty", async () => { - const result = await getTxOut(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "txid can not be empty") - }) - - it("should throw 400 if n is empty", async () => { - req.params.txid = `sometxid` - const result = await getTxOut(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "n can not be empty") - }) - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - req.params.n = 0 - - const result = await getTxOut(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") - assert.include( - result.error, - "Could not communicate with full node", - "Error message expected" - ) - }) - - // This test can only run for unit tests. See TODO at the top of this file. - it("should GET /getTxOut", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockTxOut }) - } - - req.params.txid = `5747e6462e2c452a5d583fd6a5f82866cd8e4a86826c86d9a1842b7d023e0c0c` - req.params.n = 1 - - const result = await getTxOut(req, res) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.hasAllKeys(result, [ - "bestblock", - "confirmations", - "value", - "scriptPubKey", - "coinbase" - ]) - assert.hasAllKeys(result.scriptPubKey, [ - "asm", - "hex", - "reqSigs", - "type", - "addresses" - ]) - assert.isArray(result.scriptPubKey.addresses) - }) - }) - - describe("getTxOutProof()", () => { - const getTxOutProof = blockchainRoute.testableComponents.getTxOutProofSingle - - it("should throw 400 if txid is empty", async () => { - const result = await getTxOutProof(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "txid can not be empty") - }) - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - - const result = await getTxOutProof(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") - assert.include( - result.error, - "Could not communicate with full node", - "Error message expected" - ) - }) - - it("should GET /getTxOutProof", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockTxOutProof }) - } - - req.params.txid = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - - const result = await getTxOutProof(req, res) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isString(result) - }) - }) - - describe("#getTxOutProofBulk", () => { - // route handler. - const getTxOutProofBulk = - blockchainRoute.testableComponents.getTxOutProofBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await getTxOutProofBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "txids needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single txid", async () => { - req.body.txids = `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - - const result = await getTxOutProofBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "txids needs to be an array", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.txids = testArray - - const result = await getTxOutProofBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should GET proof for single txid", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockTxOutProof }) - } - - req.body.txids = [ - `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - ] - - const result = await getTxOutProofBulk(req, res) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.isString(result[0]) - }) - - it("should GET proof for multiple txids", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .times(2) - .reply(200, { result: mockData.mockTxOutProof }) - } - - req.body.txids = [ - `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde`, - `d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde` - ] - - const result = await getTxOutProofBulk(req, res) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.equal(result.length, 2, "Correct length of returned array") - }) - }) - - describe("verifyTxOutProof()", () => { - const verifyTxOutProof = - blockchainRoute.testableComponents.verifyTxOutProofSingle - - it("should throw 400 if proof is empty", async () => { - const result = await verifyTxOutProof(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "proof can not be empty") - }) - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - req.params.proof = mockData.mockTxOutProof - - const result = await verifyTxOutProof(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") - assert.include( - result.error, - "Could not communicate with full node", - "Error message expected" - ) - }) - - it("should GET /verifyTxOutProof", async () => { - const expected = - "d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde" - - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: [expected] }) - } - - req.params.proof = mockData.mockTxOutProof - - const result = await verifyTxOutProof(req, res) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.isString(result[0]) - assert.equal(result[0], expected) - }) - }) - - describe("#verifyTxOutProofBulk", () => { - // route handler. - const verifyTxOutProofBulk = - blockchainRoute.testableComponents.verifyTxOutProofBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await verifyTxOutProofBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "proofs needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single txid", async () => { - req.body.proofs = mockData.mockTxOutProof - - const result = await verifyTxOutProofBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "proofs needs to be an array", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.proofs = testArray - - const result = await verifyTxOutProofBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should get single proof", async () => { - const expected = - "d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde" - - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: [expected] }) - } - - req.body.proofs = [mockData.mockTxOutProof] - - const result = await verifyTxOutProofBulk(req, res) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.isString(result[0]) - assert.equal(result[0], expected) - }) - - it("should get multiple proofs", async () => { - const expected = - "d65881582ff2bff36747d7a0d0e273f10281abc8bd5c15df5d72f8f3fa779cde" - - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .times(2) - .reply(200, { result: [expected] }) - } - - req.body.proofs = [mockData.mockTxOutProof, mockData.mockTxOutProof] - - const result = await verifyTxOutProofBulk(req, res) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.isArray(result) - assert.isString(result[0]) - assert.equal(result[0], expected) - assert.equal(result.length, 2) - }) - }) -}) diff --git a/test/v2/control.js b/test/v2/control.js deleted file mode 100644 index e5deaa9..0000000 --- a/test/v2/control.js +++ /dev/null @@ -1,140 +0,0 @@ -/* - TESTS FOR THE CONTROL.JS LIBRARY - - This test file uses the environment variable TEST to switch between unit - and integration tests. By default, TEST is set to 'unit'. Set this variable - to 'integration' to run the tests against BCH mainnet. - -*/ - -"use strict" - -const chai = require("chai") -const assert = chai.assert -const controlRoute = require("../../src/routes/v2/control") -const nock = require("nock") // HTTP mocking - -let originalEnvVars // Used during transition from integration to unit tests. - -// Mocking data. -const { mockReq, mockRes } = require("./mocks/express-mocks") -const mockData = require("./mocks/control-mock") - -// Used for debugging. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -describe("#ControlRouter", () => { - let req, res - - before(() => { - // Save existing environment variables. - originalEnvVars = { - BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL, - RPC_BASEURL: process.env.RPC_BASEURL, - RPC_USERNAME: process.env.RPC_USERNAME, - RPC_PASSWORD: process.env.RPC_PASSWORD - } - - // Set default environment variables for unit tests. - if (!process.env.TEST) process.env.TEST = "unit" - if (process.env.TEST === "unit") { - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - process.env.RPC_BASEURL = "http://fakeurl/api" - process.env.RPC_USERNAME = "fakeusername" - process.env.RPC_PASSWORD = "fakepassword" - } - }) - - // Setup the mocks before each test. - beforeEach(() => { - // Mock the req and res objects used by Express routes. - req = mockReq - res = mockRes - - // Explicitly reset the parmas and body. - req.params = {} - req.body = {} - req.query = {} - - // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() - }) - - afterEach(() => { - // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() - }) - - after(() => { - // Restore any pre-existing environment variables. - process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL - process.env.RPC_BASEURL = originalEnvVars.RPC_BASEURL - process.env.RPC_USERNAME = originalEnvVars.RPC_USERNAME - process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD - }) - - describe("#root", async () => { - // root route handler. - const root = controlRoute.testableComponents.root - - it("should respond to GET for base route", async () => { - const result = root(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(result.status, "control", "Returns static string") - }) - }) - - describe("#GetInfo", () => { - const getInfo = controlRoute.testableComponents.getInfo - - it("should throw 500 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - const result = await getInfo(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or greater expected." - ) - //assert.include(result.error, "ENOTFOUND", "Error message expected") - }) - - it("should get info on the full node", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockGetInfo }) - } - - const result = await getInfo(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAnyKeys(result, [ - "version", - "protocolversion", - "blocks", - "timeoffset", - "connections", - "proxy", - "difficulty", - "testnet", - "paytxfee", - "relayfee", - "errors" - ]) - }) - }) -}) diff --git a/test/v2/e2e/address.js b/test/v2/e2e/address.js deleted file mode 100644 index 878ad9e..0000000 --- a/test/v2/e2e/address.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - End-to-end tests for the Address endpoint. - These tests assume that the repo is running locally and pointed at TESTNET -*/ - -"use strict" - -const rp = require("request-promise") - -const rawtransactions = require("../../../dist/routes/v2/address") - -/* - This unconfirmed utxo test needs to be expanded in the future to automatically - create a transaction. For now, that is done manually and the address provided - as a constant. - - 1. Start rest running locally, pointed at TESTNET. - 2. Fund each address with a small amount of tBCH - 3. Run this program with `node address.js` - 4. If successful, it will return the unconfirmed UTXOs. -*/ -const addr1 = "bchtest:qpdcp5pv7qphu5tsjfgwezld9n9aq9ue6swgqpf45c" -const addr2 = "bchtest:qzpvx999fagau0xqmvu3xvll9evapge7u5hcgeknzv" - -async function testSingleUnconfirmed() { - try { - const options = { - method: "GET", - uri: `http://localhost:3000/v2/address/unconfirmed/${addr1}`, - resolveWithFullResponse: true, - json: true - } - - const result = await rp(options) - // console.log(`result.body: ${JSON.stringify(result.body, null, 2)}`) - } catch (err) { - // console.log(`Error: `, err) - } -} -testSingleUnconfirmed() - -async function testDoubleUnconfirmed() { - try { - const options = { - method: "POST", - uri: `http://localhost:3000/v2/address/unconfirmed`, - resolveWithFullResponse: true, - json: true, - body: { - addresses: [addr1, addr2] - } - } - - const result = await rp(options) - // console.log(`result.body: ${JSON.stringify(result.body, null, 2)}`) - } catch (err) { - // console.log(`Error: `, err) - } -} -testDoubleUnconfirmed() diff --git a/test/v2/helpers/panda.js b/test/v2/helpers/panda.js deleted file mode 100644 index 48a7d7f..0000000 --- a/test/v2/helpers/panda.js +++ /dev/null @@ -1,29 +0,0 @@ -"use strict" - -/** - * Read more about panda here: https://panda-suite.github.io/ - */ -const panda = require("pandacash-core") - -const runLocalNode = done => { - const server = panda.server({ - // always the same mnemonic - // mnemonic: "cigar magnet ocean purchase travel damp snack alone theme budget wagon wrong", - seedAccounts: true, - enableLogs: false, - debug: false - }) - - server.listen({ - port: 48332, - walletPort: 48333, - }, (err, pandaCashCore) => { - if (err) return console.error(err) - - done() - }) -} - -module.exports = { - runLocalNode -} diff --git a/test/v2/integration/README.md b/test/v2/integration/README.md deleted file mode 100644 index 3bd22f8..0000000 --- a/test/v2/integration/README.md +++ /dev/null @@ -1,7 +0,0 @@ -This directory contains integration tests. To run them requires that -a local copy of rest.bitcoin.com is running. The tests in this directory are -exactly the same calls that end users would use for their app to interact -directly with rest.bitcoin.com. - -At present, this directory focuses on raw-transations. But more integration tests -will be added. diff --git a/test/v2/integration/rate-limits.js b/test/v2/integration/rate-limits.js deleted file mode 100644 index b61c25e..0000000 --- a/test/v2/integration/rate-limits.js +++ /dev/null @@ -1,94 +0,0 @@ -/* - */ - -"use strict" - -const chai = require("chai") -const assert = chai.assert -const axios = require("axios") - -// Used for debugging. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -const SERVER = `https://rest.btctest.net/v2/` -//const SERVER = `http://localhost:3000/v2/` - -describe("#rate limits", () => { - it("should get control/getInfo() with no auth", async () => { - const options = { - method: "GET", - url: `${SERVER}control/getInfo` - } - - const result = await axios(options) - //console.log(`result.status: ${result.status}`) - //console.log(`result.data: ${util.inspect(result.data)}`) - - assert.equal(result.status, 200) - assert.hasAnyKeys(result.data, ["version"]) - }) - - it("should trigger rate-limit handler if rate limits exceeds 60 request per minute", async () => { - try { - // Actual rate limit is 60 per minute X 4 nodes = 240 rpm. - const options = { - method: "GET", - url: `${SERVER}control/getInfo` - } - - const promises = [] - for (let i = 0; i < 250; i++) { - const promise = axios(options) - promises.push(promise) - } - - await Promise.all(promises) - - assert.equal(true, false, "Unexpected result!") - } catch (err) { - //console.log(`err.response: ${util.inspect(err.response)}`) - - assert.equal(err.response.status, 429) - assert.include(err.response.data.error, "Too many requests") - } - }) - - it("should not trigger rate-limit handler if correct pro-tier password is used", async () => { - try { - const username = "BITBOX" - - // Pro-tier is accessed by using the right password. - const password = "BITBOX" - //const password = "something" - - const combined = `${username}:${password}` - const base64Credential = Buffer.from(combined).toString("base64") - const readyCredential = `Basic ${base64Credential}` - - const options = { - method: "GET", - url: `${SERVER}control/getInfo`, - headers: { Authorization: readyCredential } - } - - const promises = [] - for (let i = 0; i < 250; i++) { - const promise = axios(options) - promises.push(promise) - } - - await Promise.all(promises) - - assert.equal(true, true, "Not throwing an error is a pass!") - } catch (err) { - // console.log(`err.response: ${util.inspect(err.response)}`) - - assert.equal( - true, - false, - "This error handler should not have been triggered. Is the password correct?" - ) - } - }) -}) diff --git a/test/v2/integration/raw-transactions.js b/test/v2/integration/raw-transactions.js deleted file mode 100644 index 7c17347..0000000 --- a/test/v2/integration/raw-transactions.js +++ /dev/null @@ -1,191 +0,0 @@ -/* - These integration tests are intended to be run against a live local copy of - rest.bitcoin.com. They exercise the endpoints in the same way the SDK or - end-user application would. These tests were created to replace the parts - removed from the swagger UI, that otherwise would have excersiced these endpoints. - - TODO: - -/rawtransactions/sendRawTransaction is more appropropriate for an e2e test, - so it is omitted here. -*/ - -"use strict" - -const chai = require("chai") -const assert = chai.assert -const rawtransactions = require("../../../dist/routes/v2/rawtransactions") -const rp = require("request-promise") - -// Used for debugging. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -const mockData = require("../mocks/raw-transactions-mocks") - -describe("#Raw-Transactions", () => { - describe("#root", () => { - it(`should return root`, async () => { - const options = { - method: "GET", - uri: `http://localhost:3000/v2/rawtransactions/`, - resolveWithFullResponse: true, - json: true - } - - const result = await rp(options) - //console.log(`result: ${JSON.stringify(result, null, 0)}`) - - assert.equal(result.body.status, "rawtransactions") - }) - }) - - describe("#whCreateTx", () => { - it(`should return tx hex`, async () => { - const minIn = { - txid: - "f7ed9cf23dee85910f6269c9a101a75fcfd2f3c6fc81f17fad824ff7aaf99ab2", - vout: 1 - } - - const options = { - method: "PUT", - uri: `http://localhost:3000/v2/rawtransactions/create`, - resolveWithFullResponse: true, - json: true, - body: { - //inputs: [mockData.mockWHCreateInput], - inputs: [minIn], - outputs: {} - } - } - - const result = await rp(options) - //console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`) - - assert.equal( - result.body, - "0200000001b29af9aaf74f82ad7ff181fcc6f3d2cf5fa701a1c969620f9185ee3df29cedf70100000000ffffffff0000000000" - ) - }) - }) - - describe("#whOpReturn", () => { - it(`should return tx hex`, async () => { - const options = { - method: "PUT", - uri: `http://localhost:3000/v2/rawtransactions/opreturn`, - resolveWithFullResponse: true, - json: true, - body: { - rawtx: "01000000000000000000", - payload: "00000000000000020000000006dac2c0" - } - } - - const result = await rp(options) - //console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`) - - assert.equal( - result.body, - "0100000000010000000000000000166a140877686300000000000000020000000006dac2c000000000" - ) - }) - }) - - describe("#whReference", () => { - it(`should return tx hex`, async () => { - const options = { - method: "PUT", - uri: `http://localhost:3000/v2/rawtransactions/reference`, - resolveWithFullResponse: true, - json: true, - body: { - rawtx: - "0100000001a7a9402ecd77f3c9f745793c9ec805bfa2e14b89877581c734c774864247e6f50400000000ffffffff03aa0a0000000000001976a9146d18edfe073d53f84dd491dae1379f8fb0dfe5d488ac5c0d0000000000004751210252ce4bdd3ce38b4ebbc5a6e1343608230da508ff12d23d85b58c964204c4cef3210294cc195fc096f87d0f813a337ae7e5f961b1c8a18f1f8604a909b3a5121f065b52aeaa0a0000000000001976a914946cb2e08075bcbaf157e47bcb67eb2b2339d24288ac00000000", - destination: "bitcoincash:qrn60nerx5zug4u4hal06atep3lzhtecvy4pxk75lf", - amount: 0.005 - } - } - - const result = await rp(options) - //console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`) - - assert.isString(result.body) - }) - }) - - describe("#whChangeOutput", () => { - it(`should return tx hex`, async () => { - const options = { - method: "PUT", - uri: `http://localhost:3000/v2/rawtransactions/change`, - resolveWithFullResponse: true, - json: true, - body: { - rawtx: - "0100000001b15ee60431ef57ec682790dec5a3c0d83a0c360633ea8308fbf6d5fc10a779670400000000ffffffff025c0d00000000000047512102f3e471222bb57a7d416c82bf81c627bfcd2bdc47f36e763ae69935bba4601ece21021580b888ff56feb27f17f08802ebed26258c23697d6a462d43fc13b565fda2dd52aeaa0a0000000000001976a914946cb2e08075bcbaf157e47bcb67eb2b2339d24288ac00000000", - destination: "bitcoincash:qrn60nerx5zug4u4hal06atep3lzhtecvy4pxk75lf", - prevtxs: [ - { - txid: - "6779a710fcd5f6fb0883ea3306360c3ad8c0a3c5de902768ec57ef3104e65eb1", - vout: 4, - scriptPubKey: - "76a9147b25205fd98d462880a3e5b0541235831ae959e588ac", - value: 0.00068257 - } - ], - fee: 0.000035 - } - } - - const result = await rp(options) - //console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`) - - assert.isString(result.body) - }) - }) - - describe("#whInput", () => { - it(`should return tx hex`, async () => { - const options = { - method: "PUT", - uri: `http://localhost:3000/v2/rawtransactions/input`, - resolveWithFullResponse: true, - json: true, - body: { - txid: - "b006729017df05eda586df9ad3f8ccfee5be340aadf88155b784d1fc0e8342ee", - n: 0 - } - } - - const result = await rp(options) - //console.log(`result.body: ${JSON.stringify(result.body, null, 0)}`) - - assert.isString(result.body) - }) - }) - - describe("#getRawTransaction", () => { - it(`should return tx hex`, async () => { - const options = { - method: "POST", - uri: `http://localhost:3000/v2/rawtransactions/getRawTransaction`, - resolveWithFullResponse: true, - json: true, - body: { - txids: [ - "0e3e2357e806b6cdb1f70b54c3a3a17b6714ee1f0e68bebb44a74b1efd512098" - ], - verbose: true - } - } - - const result = await rp(options) - // console.log(`result.body: ${util.inspect(result.body)}`) - - assert.isArray(result.body) - }) - }) -}) diff --git a/test/v2/mining.js b/test/v2/mining.js deleted file mode 100644 index 14e1163..0000000 --- a/test/v2/mining.js +++ /dev/null @@ -1,176 +0,0 @@ -/* - TESTS FOR THE MINING.TS LIBRARY - - This test file uses the environment variable TEST to switch between unit - and integration tests. By default, TEST is set to 'unit'. Set this variable - to 'integration' to run the tests against BCH mainnet. -*/ - -"use strict" - -const chai = require("chai") -const assert = chai.assert -const miningRoute = require("../../src/routes/v2/mining") -const nock = require("nock") // HTTP mocking - -let originalEnvVars // Used during transition from integration to unit tests. - -// Mocking data. -const { mockReq, mockRes } = require("./mocks/express-mocks") -const mockData = require("./mocks/mining-mocks") - -// Used for debugging. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -describe("#Mining", () => { - let req, res - - before(() => { - // Save existing environment variables. - originalEnvVars = { - BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL, - RPC_BASEURL: process.env.RPC_BASEURL, - RPC_USERNAME: process.env.RPC_USERNAME, - RPC_PASSWORD: process.env.RPC_PASSWORD - } - - // Set default environment variables for unit tests. - if (!process.env.TEST) process.env.TEST = "unit" - if (process.env.TEST === "unit") { - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - process.env.RPC_BASEURL = "http://fakeurl/api" - process.env.RPC_USERNAME = "fakeusername" - process.env.RPC_PASSWORD = "fakepassword" - } - }) - - // Setup the mocks before each test. - beforeEach(() => { - // Mock the req and res objects used by Express routes. - req = mockReq - res = mockRes - - // Explicitly reset the parmas and body. - req.params = {} - req.body = {} - req.query = {} - - // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() - }) - - afterEach(() => { - // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() - }) - - after(() => { - // Restore any pre-existing environment variables. - process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL - process.env.RPC_BASEURL = originalEnvVars.RPC_BASEURL - process.env.RPC_USERNAME = originalEnvVars.RPC_USERNAME - process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD - }) - - describe("#root", async () => { - // root route handler. - const root = miningRoute.testableComponents.root - - it("should respond to GET for base route", async () => { - const result = root(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(result.status, "mining", "Returns static string") - }) - }) - - describe("#getMiningInfo", async () => { - const getMiningInfo = miningRoute.testableComponents.getMiningInfo - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - const result = await getMiningInfo(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or greater expected." - ) - //assert.include(result.error,"Network error: Could not communicate with full node","Error message expected") - }) - - it("should GET mining information", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockMiningInfo }) - } - - const result = await getMiningInfo(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, [ - "blocks", - "currentblocksize", - "currentblocktx", - "difficulty", - "blockprioritypercentage", - "errors", - "networkhashps", - "pooledtx", - "chain" - ]) - }) - }) - - describe("#getNetworkHashPS", async () => { - const getNetworkHashPS = miningRoute.testableComponents.getNetworkHashPS - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - const result = await getNetworkHashPS(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or greater expected." - ) - //assert.include(result.error,"Network error: Could not communicate with full node","Error message expected") - }) - - it("should GET Network Hash per second", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: 517604755.6648782 }) - } - - const result = await getNetworkHashPS(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isNumber(result) - }) - }) -}) diff --git a/test/v2/mocks/address-mock.js b/test/v2/mocks/address-mock.js deleted file mode 100644 index 7d4e3b9..0000000 --- a/test/v2/mocks/address-mock.js +++ /dev/null @@ -1,160 +0,0 @@ -/* - This library contains mocking data for running unit tests on the address route. -*/ - -"use strict" - -const mockAddressDetails = { - addrStr: "1Fg4r9iDrEkCcDmHTy2T79EusNfhyQpu7W", - balance: 0.00126419, - balanceSat: 126419, - totalReceived: 0.02175868, - totalReceivedSat: 2175868, - totalSent: 0.02049449, - totalSentSat: 2049449, - unconfirmedBalance: 0, - unconfirmedBalanceSat: 0, - unconfirmedTxApperances: 0, - txApperances: 3, - transactions: [ - "2dc053f55a666a3d2a08b1c680b704d62a55506d14ad884add87edcc56b9277d", - "544c15ce35c0f2e808d28f29d6587f1ec9276233e29856b7f2938cf0daef0026", - "81039b1d7b855b133f359f9dc65f776bd105650153a941675fedc504228ddbd3" - ], - legacyAddress: "1Fg4r9iDrEkCcDmHTy2T79EusNfhyQpu7W", - cashAddress: "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -} - -const mockUtxoDetails = [ - { - txid: "15f6a584080b04911121fbaca7bfcf3dd64ef2bfa5a01daf31e05a296c3e5e9e", - vout: 286, - amount: 0.00001, - satoshis: 1000, - height: 546083, - confirmations: 3490 - }, - { - txid: "15f6a584080b04911121fbaca7bfcf3dd64ef2bfa5a01daf31e05a296c3e5e9e", - vout: 287, - amount: 0.00001, - satoshis: 1000, - height: 546083, - confirmations: 3490 - }, - { - txid: "15f6a584080b04911121fbaca7bfcf3dd64ef2bfa5a01daf31e05a296c3e5e9e", - vout: 288, - amount: 0.00001, - satoshis: 1000, - height: 546083, - confirmations: 3490 - } -] - -const mockUnconfirmed = [ - { - address: "1EzdL6TBbkNhnB2fYiBaKmcs5fxaoqwdAp", - txid: "000c00a90fb5031da6e02f7625df2f8b35a4c16e6feb9fc72293e67e5ff75786", - vout: 0, - scriptPubKey: "76a914997fabcd94a1e2aaa13a7664362e5e7b96c169a988ac", - amount: 0.00999626, - satoshis: 999626, - confirmations: 0, - ts: 1537989425 - } -] - -const mockTransactions = { - pagesTotal: 1, - txs: [ - { - txid: "000c00a90fb5031da6e02f7625df2f8b35a4c16e6feb9fc72293e67e5ff75786", - version: 2, - locktime: 549565, - vin: [ - { - txid: - "45c891c6d44619fc85716ba1b593aa83ebc1e500fe611b1ab98531ea203a0f21", - vout: 209, - sequence: 4294967294, - n: 0, - scriptSig: { - hex: - "47304402205b136f348bedae61a87c0500979d47ab02c76f0e4aba866b94c4931fccb5d7dc0220757a141660475b0cd4cb8b1f54b1ace95105f5bea775e817b115fb589fe4477541210246daec651c506f353daa7468714399c773df42ac4d663022d0e446a3331ac64a", - asm: - "304402205b136f348bedae61a87c0500979d47ab02c76f0e4aba866b94c4931fccb5d7dc0220757a141660475b0cd4cb8b1f54b1ace95105f5bea775e817b115fb589fe44775[ALL|FORKID] 0246daec651c506f353daa7468714399c773df42ac4d663022d0e446a3331ac64a" - }, - addr: "14TdovsQUL69xL5g3zSzhLpmDb93d9rU9m", - valueSat: 1979534, - value: 0.01979534, - doubleSpentTxID: null - }, - { - txid: - "6e5fa79547112a912adf6082c975dc80e282045193512ea369adb6bac7420674", - vout: 147, - sequence: 4294967294, - n: 1, - scriptSig: { - hex: - "473044022056674ac83a37d54ffec2c6d33cfc2ae2256f821b8bd818e1b6783356b49f3d28022046d3dd147c8fc51f2da5d16996a3cea1d7314e327aa1ae1017418737ed20136a4121038f23af565f68d8455e07e5c33c5cfc5114de2f478f035db23eb02f8ff5fe7f6e", - asm: - "3044022056674ac83a37d54ffec2c6d33cfc2ae2256f821b8bd818e1b6783356b49f3d28022046d3dd147c8fc51f2da5d16996a3cea1d7314e327aa1ae1017418737ed20136a[ALL|FORKID] 038f23af565f68d8455e07e5c33c5cfc5114de2f478f035db23eb02f8ff5fe7f6e" - }, - addr: "1CEtC2fjELvmAzTd6MWuHb1gvuSq8x3Xpb", - valueSat: 10466, - value: 0.00010466, - doubleSpentTxID: null - } - ], - vout: [ - { - value: "0.00999626", - n: 0, - scriptPubKey: { - hex: "76a914997fabcd94a1e2aaa13a7664362e5e7b96c169a988ac", - asm: - "OP_DUP OP_HASH160 997fabcd94a1e2aaa13a7664362e5e7b96c169a9 OP_EQUALVERIFY OP_CHECKSIG", - addresses: ["1EzdL6TBbkNhnB2fYiBaKmcs5fxaoqwdAp"], - type: "pubkeyhash" - }, - spentTxId: null, - spentIndex: null, - spentHeight: null - }, - { - value: "0.00990000", - n: 1, - scriptPubKey: { - hex: "76a914e3335e4d6babe61ea58311293c1c6bfe802801cb88ac", - asm: - "OP_DUP OP_HASH160 e3335e4d6babe61ea58311293c1c6bfe802801cb OP_EQUALVERIFY OP_CHECKSIG", - addresses: ["1MiKvgaQTFCTEZbSDMZgw9ahaB52Cr4ofb"], - type: "pubkeyhash" - }, - spentTxId: null, - spentIndex: null, - spentHeight: null - } - ], - blockhash: - "00000000000000000158b1a2883688873ec5ea076e3b0f576bcdfe1fd277880f", - blockheight: 549601, - confirmations: 5, - time: 1537990026, - blocktime: 1537990026, - valueOut: 0.01989626, - size: 372, - valueIn: 0.0199, - fees: 0.00000374 - } - ] -} - -module.exports = { - mockAddressDetails, - mockUtxoDetails, - mockUnconfirmed, - mockTransactions -} diff --git a/test/v2/mocks/block-mock.js b/test/v2/mocks/block-mock.js deleted file mode 100644 index 069b90d..0000000 --- a/test/v2/mocks/block-mock.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - This library contains mocking data for running unit tests on the address route. -*/ - -"use strict" - -const mockBlockDetails = { - hash: "00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79", - size: 1319, - height: 1267544, - version: 536870912, - merkleroot: - "c2cd01ec2cf149acc7631385f89ae103d1a2ad212ab810c862d9680a811618ce", - tx: [ - "52bfa89d449fef8070ec33e7a61f5ec8b5417ff62b050cd6a144ebce9557e0fa", - "8988e8742d2523f667c2d1374861919e3a82903e059fec75c10795da2303b93b", - "41def004f22b196d675566a6983ecf97611e72c48375b8ba779983d5ccd369d7", - "8ca8efb06abb94395f8331f87e101504dcf66aee828f2111922f056971fa7502", - "9ab409cbb203be7cf22bd6598d787fcdd5b417d885b72207ecc7253470770649" - ], - time: 1542048973, - nonce: 3936284753, - bits: "1a03c148", - difficulty: 4467892.99177529, - chainwork: "00000000000000000000000000000000000000000000003ee27b503a064045bd", - confirmations: 3, - previousblockhash: - "00000000000001891202cbe18729a02a8763cf04da0ca7aded6e6c7d9b500785", - nextblockhash: - "000000000000039b93b3f3403a0eb21150904b15229d998cf3788fd8bf192cc2", - reward: 0.78125, - isMainChain: true, - poolInfo: {} -} - -const mockBlockHash = - "00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79" - -module.exports = { - mockBlockDetails, - mockBlockHash -} diff --git a/test/v2/mocks/blockchain-mock.js b/test/v2/mocks/blockchain-mock.js deleted file mode 100644 index 6025583..0000000 --- a/test/v2/mocks/blockchain-mock.js +++ /dev/null @@ -1,271 +0,0 @@ -/* - This library contains mocking data for running unit tests on the address route. -*/ - -"use strict" - -const mockBlockHash = - "00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79" - -const mockBlockchainInfo = { - chain: "test", - blocks: 1267694, - headers: 1267694, - bestblockhash: - "000000000000013eaf80d1e157e32804c36a58ad0bb26ca59833880c80298780", - difficulty: 4105763.969035785, - mediantime: 1542131305, - verificationprogress: 0.9999968911571566, - chainwork: "00000000000000000000000000000000000000000000003f0443b0b5f02ce255", - pruned: false, - softforks: [ - { id: "bip34", version: 2, reject: { status: true } }, - { id: "bip66", version: 3, reject: { status: true } }, - { id: "bip65", version: 4, reject: { status: true } } - ], - bip9_softforks: { - csv: { - status: "active", - startTime: 1456790400, - timeout: 1493596800, - since: 770112 - } - } -} - -const mockChainTips = [ - { - height: 1267696, - hash: "000000000000035bfc43a642ebbff8cfc1e88b1d564de8b0a6c7e2797eeafb21", - branchlen: 0, - status: "active" - }, - { - height: 1267581, - hash: "000000000001bd12e4124207682563135b21353ca3087bc6b0c409f7f9e1da91", - branchlen: 1, - status: "valid-fork" - }, - { - height: 1267375, - hash: "00000000702036979df70236bcc45dfc72d43d5d0e6834007afa1fa627e49587", - branchlen: 1036, - status: "headers-only" - }, - { - height: 1266979, - hash: "00000000e851abbde2174ccdc5c2508b909f81f23c4b7abeac864eee1a4891c7", - branchlen: 1, - status: "valid-fork" - }, - { - height: 1266973, - hash: "000000007e1324d2900ed70947f89ab8fd4a267e87c9244c333215659a7370d5", - branchlen: 1, - status: "valid-fork" - }, - { - height: 1266967, - hash: "000000000f37f12843d4800f50ec4de44dce0432e4d448242d43ff04a7a2948d", - branchlen: 1, - status: "valid-fork" - }, - { - height: 1266963, - hash: "00000000ae11ec99e88898cbd72567cb4cb79e7fc13243357bef79632769deb4", - branchlen: 1, - status: "valid-fork" - }, - { - height: 1266962, - hash: "000000004f97851259b4460a049e44fa5bd4beadee04a85bf81bd2e90c4f24f9", - branchlen: 1, - status: "valid-fork" - }, - { - height: 1266950, - hash: "00000000d12a0e4a827f73ecaadb563df7e99817294939f2be8e943079dc60b2", - branchlen: 1, - status: "valid-fork" - }, - { - height: 1266944, - hash: "00000000612d16af273217818e8e7ac5014a19f68ed04e1bc897b1be0a9c744f", - branchlen: 1, - status: "valid-fork" - }, - { - height: 1266941, - hash: "0000000000002b91cdb15cacf3368da2c9ffce511d64092193b507dad75f4a27", - branchlen: 1, - status: "valid-fork" - }, - { - height: 1266896, - hash: "000000000002745f67a66617b1f9f65c2dfb4c4b8a9ed9b1320b98d3ca46cbe5", - branchlen: 1, - status: "valid-fork" - }, - { - height: 1266481, - hash: "0000000000036babee3ba7654cddb29a9fe5e85c9fbeb44bb53d5ffb694b9670", - branchlen: 1, - status: "valid-fork" - }, - { - height: 1266452, - hash: "00000000502c3ab1490e0839780a4b441bbe21507c454545941e6adcde73c2ff", - branchlen: 1, - status: "valid-fork" - }, - { - height: 1266336, - hash: "000000001c9a0b7cd9eb2210f69d5a9a3f4546ba3707deb40993d729190582d7", - branchlen: 10, - status: "headers-only" - }, - { - height: 1266097, - hash: "000000000028ac0fbd18afe6f001f70cb7549bd2b3082d754dde88ee236368a2", - branchlen: 1, - status: "valid-fork" - }, - { - height: 1265522, - hash: "00000000000000c6df7a2063030e6ce51399e470cc46e4bf6d67aa3f0feca98d", - branchlen: 1, - status: "valid-fork" - }, - { - height: 1265470, - hash: "0000000000168e6442a595a02340fccf8dc2b0e8912c632d39e1f870685f6d7c", - branchlen: 2, - status: "valid-fork" - }, - { - height: 1265275, - hash: "000000008bc2b37ae6af1b886b52e7e1c1122c12badc4ae6c13df64f789267cf", - branchlen: 11027, - status: "headers-only" - }, - { - height: 1265257, - hash: "00000000005bc3e9973d7273211eab02ae02549c7ebae48764c0bd648d4d7bbe", - branchlen: 114, - status: "valid-fork" - }, - { - height: 1265254, - hash: "0000000000e5b909d3541857315e9ee103b042d9ba661f25cc3e8fb25a96c8ce", - branchlen: 111, - status: "valid-fork" - }, - { - height: 1265245, - hash: "000000000066eb8832f6f990baceb379536f437216e22475a6c1b9133d250cb8", - branchlen: 102, - status: "valid-fork" - }, - { - height: 1264455, - hash: "00000000000001befc1bb17a23208d0b77b2449e37093ce8fbb5e346b19cafcc", - branchlen: 1, - status: "valid-headers" - }, - { - height: 1264373, - hash: "00000000000001ed3747e04fb95054cac0358fb4d8009d1999fc3ea34413e84a", - branchlen: 175, - status: "valid-fork" - }, - { - height: 1263912, - hash: "00000000000004635aecbcb97edcd6f5396483338838cb40da804eb354c68bde", - branchlen: 1, - status: "valid-fork" - }, - { - height: 1262906, - hash: "00000000b31643d92a3e7c38e9755844fc8607e3f1055580a8ff28854e8a8ece", - branchlen: 1, - status: "valid-fork" - }, - { - height: 1255749, - hash: "000000001dde5b015a99137f4cba87871370f4b6fcfbcc8b126547e6c33177da", - branchlen: 129, - status: "headers-only" - }, - { - height: 1188789, - hash: "00000000af942ce4eb60b3213cbcb7c98a7330f1f8f1adb4b6376f5e822e15b2", - branchlen: 92, - status: "headers-only" - } -] - -const mockMempoolInfo = { - size: 87, - bytes: 16816, - usage: 66408, - maxmempool: 300000000, - mempoolminfee: 0 -} - -const mockRawMempool = [ - "db045bc3bd1088fa91f5ebb05c35cb9e2a91a22377f79b465cc6920b9893123c", - "6b3df7febf2b9834f1409155f88b866dd516b36376eae00e2b455df82e290405" -] - -const mockBlockHeaderConcise = - "0000ff7f7d217c9b7845ea8b50d620c59a1bf7c276566406e9b7bc7e463e0000000000006d70322c0b697c1c81d2744f87f09f1e9780ba5d30338952e2cdc64e60456f8423bb0a5ceafa091a3e843526" - -const mockBlockHeader = { - hash: "00000000000008c3679777df34f1a09565f98b2400a05b7c8da72525fdca3900", - confirmations: 7, - height: 1272859, - version: 2147418112, - versionHex: "7fff0000", - merkleroot: - "846f45604ec6cde2528933305dba80971e9ff0874f74d2811c7c690b2c32706d", - time: 1544207139, - mediantime: 1544202884, - nonce: 641041470, - bits: "1a09faea", - difficulty: 1681035.704111868, - chainwork: "00000000000000000000000000000000000000000000003fc4752e608403be04", - previousblockhash: - "0000000000003e467ebcb7e906645676c2f71b9ac520d6508bea45789b7c217d", - nextblockhash: - "00000000000006899041cdfd6c0b73a97730c362346dde479b77414ad7f25ace" -} - -const mockTxOut = { - bestblock: "00000000003a7f19730dd9f172d7466658a5c8833dd03ed8f4ba36e3d857b5e7", - confirmations: 10, - value: 0.0001, - scriptPubKey: { - asm: - "OP_DUP OP_HASH160 0ee020c07f39526ac5505c54fa1ab98490979b83 OP_EQUALVERIFY OP_CHECKSIG", - hex: "76a9140ee020c07f39526ac5505c54fa1ab98490979b8388ac", - reqSigs: 1, - type: "pubkeyhash", - addresses: ["bchtest:qq8wqgxq0uu4y6k92pw9f7s6hxzfp9umsvtg39pzqf"] - }, - coinbase: false -} - -const mockTxOutProof = - "000000200798039affceb7a381e15dc62443c286efe7cae852393b656807000000000000cf9a7d6c654fd1c4558229a619a550ff749b373fa0f0027eed68b1abf6175d5c07c50f5cffff001d0452403b34000000070d0765da76980d542b5670d27ccda9e717073270f8921ad017deb7ee83fe6c4c22830a48cc8392197f1742a3d81805bd2aa4eb7469f38c74f674429d682cb87efcff1713fd0c388a50f0d11adf05363397ef79d16cdb786d47c941c2cb89f2588f42aced9c05f4203b4440616a9e4266ec47909b9580a643f402b5d4e82cc3cddd73df05a33abf6af3975d973d3033d10c4168a73d58d01f685a96d5a2519cd0de9c77faf3f8725ddf155cbdc8ab8102f173e2d0a0d74767f3bff22f588158d6d5bc71c079c7659cc864819c43877635007650502eb4060fcc9feec3280a41d602ad0a" - -module.exports = { - mockBlockHash, - mockBlockchainInfo, - mockChainTips, - mockMempoolInfo, - mockRawMempool, - mockBlockHeaderConcise, - mockBlockHeader, - mockTxOut, - mockTxOutProof -} diff --git a/test/v2/mocks/control-mock.js b/test/v2/mocks/control-mock.js deleted file mode 100644 index cca9338..0000000 --- a/test/v2/mocks/control-mock.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - This library contains mocking data for running unit tests on the address route. -*/ - -"use strict" - -const mockGetInfo = { - version: 170200, - protocolversion: 70015, - walletversion: 160300, - balance: 0, - blocks: 1266726, - timeoffset: 0, - connections: 8, - proxy: "", - difficulty: 1, - testnet: true, - keypoololdest: 1536331195, - keypoolsize: 2000, - paytxfee: 0, - relayfee: 0.00001, - errors: "Warning: unknown new rules activated (versionbit 28)" -} - -module.exports = { - mockGetInfo -} diff --git a/test/v2/mocks/express-mocks.js b/test/v2/mocks/express-mocks.js deleted file mode 100644 index fcb545f..0000000 --- a/test/v2/mocks/express-mocks.js +++ /dev/null @@ -1,88 +0,0 @@ -/* - Contains mocks of Express req and res objects. -*/ - -"use strict" - -const sinon = require("sinon") - -// Inspect JS Objects. -const util = require("util") -util.inspect.defaultOptions = { - showHidden: true, - colors: true -} - -// mock for res.send() -function fakeSend(arg) { - //console.log(`res.send: ${util.inspect(arg)}`); - mockRes.output = arg - return arg -} - -// mock for res.json() -function fakeJson(arg) { - //console.log(`res.json: ${util.inspect(arg)}`); - mockRes.output = arg - return arg -} - -// mock for res.setStatus(num) -const setStatusCode = arg => { - mockRes.statusCode = arg -} - -const mockReq = { - accepts: sinon.stub().returns({}), - acceptsCharsets: sinon.stub().returns({}), - acceptsEncodings: sinon.stub().returns({}), - acceptsLanguages: sinon.stub().returns({}), - body: {}, - flash: sinon.stub().returns({}), - get: sinon.stub().returns({}), - is: sinon.stub().returns({}), - params: {}, - query: {}, - session: {}, - locals: {}, - headers: {} -} - -const mockRes = { - append: sinon.stub().returns({}), - attachement: sinon.stub().returns({}), - clearCookie: sinon.stub().returns({}), - cookie: sinon.stub().returns({}), - download: sinon.stub().returns({}), - end: sinon.stub().returns({}), - format: {}, - get: sinon.stub().returns({}), - headersSent: sinon.stub().returns({}), - json: sinon.stub().callsFake(fakeJson), - jsonp: sinon.stub().returns({}), - links: sinon.stub().returns({}), - locals: {}, - location: sinon.stub().returns({}), - output: null, // Used for retrieving output data. - redirect: sinon.stub().returns({}), - render: sinon.stub().returns({}), - send: sinon.stub().callsFake(fakeSend), - sendFile: sinon.stub().returns({}), - sendStatus: sinon.stub().returns({}), - set: sinon.stub().returns({}), - status: sinon.stub().callsFake(setStatusCode), - statusCode: null, // Default value before calling stats(); - type: sinon.stub().returns({}), - vary: sinon.stub().returns({}), - write: sinon.stub().returns({}), - setHeader: sinon.stub().returns({}), - format: sinon.stub().returns({}) -} - -const mockNext = sinon.stub().returns() - -module.exports = { - mockReq, - mockRes, - mockNext -} diff --git a/test/v2/mocks/mining-mocks.js b/test/v2/mocks/mining-mocks.js deleted file mode 100644 index 8ef8ada..0000000 --- a/test/v2/mocks/mining-mocks.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - This library contains mocking data for running unit tests on the address route. -*/ - -"use strict" - -const mockMiningInfo = { - blocks: 1270185, - currentblocksize: 0, - currentblocktx: 0, - difficulty: 1, - blockprioritypercentage: 5, - errors: - "Warning: Unknown block versions being mined! It's possible unknown rules are in effect", - networkhashps: 517410290.9365583, - pooledtx: 5, - chain: "test" -} - -module.exports = { - mockMiningInfo -} diff --git a/test/v2/mocks/raw-transactions-mocks.js b/test/v2/mocks/raw-transactions-mocks.js deleted file mode 100644 index 08620a3..0000000 --- a/test/v2/mocks/raw-transactions-mocks.js +++ /dev/null @@ -1,157 +0,0 @@ -/* - This library contains mocking data for running unit tests. -*/ - -"use strict" - -const mockDecodeRawTransaction = { - txid: "a332237d82a2543af1b0e1ae3c8cea1610c290ebcaf084a7e9894a61de0be988", - hash: "a332237d82a2543af1b0e1ae3c8cea1610c290ebcaf084a7e9894a61de0be988", - size: 226, - version: 2, - locktime: 0, - vin: [ - { - txid: "21cced645eab150585ed7ca7c96edebab5793cc0a3b3b286c42fd7d6d798b5b9", - vout: 1, - scriptSig: { - asm: - "3045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd41390[ALL|FORKID] 0360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413d", - hex: - "483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413d" - }, - sequence: 4294967295 - } - ], - vout: [ - { - value: 0.0001, - n: 0, - scriptPubKey: { - asm: - "OP_DUP OP_HASH160 eb4b180def88e3f5625b2d8ae2c098ff7d85f664 OP_EQUALVERIFY OP_CHECKSIG", - hex: "76a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac", - reqSigs: 1, - type: "pubkeyhash", - addresses: [Array] - } - }, - { - value: 0.09989752, - n: 1, - scriptPubKey: { - asm: - "OP_DUP OP_HASH160 eb4b180def88e3f5625b2d8ae2c098ff7d85f664 OP_EQUALVERIFY OP_CHECKSIG", - hex: "76a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac", - reqSigs: 1, - type: "pubkeyhash", - addresses: [Array] - } - } - ] -} - -const mockDecodeScript = { - asm: - "0 0 -57 OP_NOP6 OP_LSHIFT OP_UNKNOWN OP_UNKNOWN OP_UNKNOWN c486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa OP_2OVER OP_NUMEQUALVERIFY OP_OR OP_INVERT OP_UNKNOWN OP_UNKNOWN 2ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab 67c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b [error]", - type: "nonstandard", - p2sh: "bchtest:pzy6dwfy6yf373w0dr05a6flfqksurjhwcl3awhdvm" -} - -const mockRawTransactionConcise = - "02000000014e6b52500110b1c30315b85805fb274f0f4afceffc1589f889b27709e59e987d000000006a473044022052762770baa71c1a0b9544ad0f1ea343d32c22aa87c5f8397b6852f464c15b1e02201f4390745cb470e21e0e3c14229f39fef55ea0643a4c997f99d9f3501eae09b7412103c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792ffffffff02d1778f950a0000001976a91436d2f27bbd826a86db1e93618ce3de89ef33169388ac80969800000000001976a914152ea3cd65f18cb8fa9146c84ea1a97af8f051de88ac00000000" - -const mockRawTransactionVerbose = { - hex: - "02000000014e6b52500110b1c30315b85805fb274f0f4afceffc1589f889b27709e59e987d000000006a473044022052762770baa71c1a0b9544ad0f1ea343d32c22aa87c5f8397b6852f464c15b1e02201f4390745cb470e21e0e3c14229f39fef55ea0643a4c997f99d9f3501eae09b7412103c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792ffffffff02d1778f950a0000001976a91436d2f27bbd826a86db1e93618ce3de89ef33169388ac80969800000000001976a914152ea3cd65f18cb8fa9146c84ea1a97af8f051de88ac00000000", - txid: "bd320377db7026a3dd5c7ec444596c0ee18fc25c4f34ee944adc03e432ce1971", - hash: "bd320377db7026a3dd5c7ec444596c0ee18fc25c4f34ee944adc03e432ce1971", - size: 225, - version: 2, - locktime: 0, - vin: [ - { - txid: "7d989ee50977b289f88915fceffc4a0f4f27fb0558b81503c3b1100150526b4e", - vout: 0, - scriptSig: { - asm: - "3044022052762770baa71c1a0b9544ad0f1ea343d32c22aa87c5f8397b6852f464c15b1e02201f4390745cb470e21e0e3c14229f39fef55ea0643a4c997f99d9f3501eae09b7[ALL|FORKID] 03c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792", - hex: - "473044022052762770baa71c1a0b9544ad0f1ea343d32c22aa87c5f8397b6852f464c15b1e02201f4390745cb470e21e0e3c14229f39fef55ea0643a4c997f99d9f3501eae09b7412103c346eee77a77a8d3e073dacc0532ca7a5b9747bc06d88bf091cac9f4bc8bb792" - }, - sequence: 4294967295 - } - ], - vout: [ - { - value: 454.58880465, - n: 0, - scriptPubKey: { - asm: - "OP_DUP OP_HASH160 36d2f27bbd826a86db1e93618ce3de89ef331693 OP_EQUALVERIFY OP_CHECKSIG", - hex: "76a91436d2f27bbd826a86db1e93618ce3de89ef33169388ac", - reqSigs: 1, - type: "pubkeyhash", - addresses: ["bchtest:qqmd9unmhkpx4pkmr6fkrr8rm6y77vckjvqe8aey35"] - } - }, - { - value: 0.1, - n: 1, - scriptPubKey: { - asm: - "OP_DUP OP_HASH160 152ea3cd65f18cb8fa9146c84ea1a97af8f051de OP_EQUALVERIFY OP_CHECKSIG", - hex: "76a914152ea3cd65f18cb8fa9146c84ea1a97af8f051de88ac", - reqSigs: 1, - type: "pubkeyhash", - addresses: ["bchtest:qq2jag7dvhccew86j9rvsn4p49a03uz3mcpw3d6aca"] - } - } - ], - blockhash: "000000000000026fa244de975ca89ea08008aa566564ce2e8ebb3144361b601b", - confirmations: 125, - time: 1542646373, - blocktime: 1542646373 -} - -const mockWHDecode = { - txid: "f8a9857fe3b8a288b5fcafb1b0fc196731f433add6d962f77acd7c10b970ff89", - fee: "500", - sendingaddress: "bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", - referenceaddress: "bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", - ismine: false, - version: 0, - type_int: 0, - type: "Simple Send", - propertyid: 368, - precision: "8", - amount: "10.00000000", - valid: true, - blockhash: "0000000046ba0bcef78caaa4492622176bbc563cf249ab52340a0449bc8e26f6", - blocktime: 1542814183, - positioninblock: 57, - block: 1269008, - confirmations: 2 -} - -const mockWHCreateInput = { - txid: "f7ed9cf23dee85910f6269c9a101a75fcfd2f3c6fc81f17fad824ff7aaf99ab2", - vout: 1, - scriptPubKey: "76a914a4b98b0c118de83e7d39c834445f51dce62425f588ac", - amount: 0.09984138, - satoshis: 9984138, - height: 1269011, - confirmations: 4, - legacyAddress: "mvXwPH74hW2yVTWwDwzsjGoaUAqJvWk7ZJ", - cashAddress: "bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", - value: 0.09984138 -} - -module.exports = { - mockDecodeRawTransaction, - mockDecodeScript, - mockRawTransactionConcise, - mockRawTransactionVerbose, - mockWHDecode, - mockWHCreateInput -} diff --git a/test/v2/mocks/slp-mocks.js b/test/v2/mocks/slp-mocks.js deleted file mode 100644 index 1d70df0..0000000 --- a/test/v2/mocks/slp-mocks.js +++ /dev/null @@ -1,327 +0,0 @@ -/* - This library contains mocking data for running unit tests. -*/ - -"use strict" - -const mockList = { - t: [ - { - tokenDetails: { - tokenIdHex: - "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb", - documentUri: "", - documentSha256: "", - symbol: "NAKAMOTO", - name: "NAKAMOTO", - decimals: 8 - }, - tokenStats: { - qty_valid_txns_since_genesis: 241, - qty_valid_token_utxos: 151, - qty_valid_token_addresses: 113, - qty_token_circulating_supply: "20995990", - qty_token_burned: "4010", - qty_token_minted: "21000000", - qty_satoshis_locked_up: 81900 - } - } - ] -} - -const mockSingleToken = { - t: [ - { - tokenDetails: { - decimals: 8, - tokenIdHex: - "650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a", - timestamp: "2018-08-26 07:06", - transactionType: "GENESIS", - versionType: 1, - documentUri: "", - documentSha256Hex: null, - symbol: "", - name: "TESTYCOIN", - batonVout: null, - containsBaton: false, - genesisOrMintQuantity: "9999", - sendOutputs: null - }, - tokenStats: { - block_created: 1253802, - block_last_active_send: 1253802, - block_last_active_mint: null, - qty_valid_txns_since_genesis: 2, - qty_valid_token_utxos: 0, - qty_valid_token_addresses: 0, - qty_token_minted: "9999", - qty_token_burned: "9999", - qty_token_circulating_supply: "0", - qty_satoshis_locked_up: 0, - minting_baton_status: "NEVER_CREATED" - } - } - ] -} - -const mockSingleTokenError = { - t: [] -} - -const mockSingleAddress = { - a: [ - { - _id: "5c93ed62a19119333d1595bc", - tokenDetails: { - tokenIdHex: - "6b081fcd1f78b187be1464313dac8ff257251b727a42b613552a4040870aeb29" - }, - address: "slptest:pz0qcslrqn7hr44hsszwl4lw5r6udkg6zqv7sq3kk7", - satoshis_balance: 546, - token_balance: "4616984" - } - ], - t: [ - { - tokenDetails: { - decimals: 8, - tokenIdHex: - "6b081fcd1f78b187be1464313dac8ff257251b727a42b613552a4040870aeb29", - timestamp: "2019-02-25 10:05", - transactionType: "GENESIS", - versionType: 1, - documentUri: "https://developer.bitcoin.com", - documentSha256Hex: "", - symbol: "DEV", - name: "DEVCOIN", - batonVout: 2, - containsBaton: true, - genesisOrMintQuantity: "500000000", - sendOutputs: null - }, - tokenStats: { - block_created: 571272, - block_last_active_send: 574758, - block_last_active_mint: 571272, - qty_valid_txns_since_genesis: 172, - qty_valid_token_utxos: 170, - qty_valid_token_addresses: 170, - qty_token_minted: "1000000000", - qty_token_burned: "4.99999995", - qty_token_circulating_supply: "999999995.00000005", - qty_satoshis_locked_up: 92820, - minting_baton_status: "ALIVE" - } - } - ] -} - -const mockTx = { - txid: "57b3082a2bf269b3d6f40fee7fb9c664e8256a88ca5ee2697c05b9457822d446", - version: 2, - locktime: 0, - vin: [ - { - txid: "61e71554a3dc18158f30d9e8f5c9b6641a789690b32302899f81cbea9fe3bb49", - vout: 2, - sequence: 4294967295, - n: 0, - scriptSig: { - hex: - "4730440220409e79fec552f01203f41d3d621ae3db89c720af261c8268ce5f0453de009f5d022001e7ffefeba7b0716d32ea55cb6ace267b6ee9cbcc8a017bb9c3b6acf7889418412103c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585", - asm: - "30440220409e79fec552f01203f41d3d621ae3db89c720af261c8268ce5f0453de009f5d022001e7ffefeba7b0716d32ea55cb6ace267b6ee9cbcc8a017bb9c3b6acf7889418[ALL|FORKID] 03c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585" - }, - value: 546, - legacyAddress: "mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL", - cashAddress: "bchtest:qz4qnxcxwvmacgye8wlakhz0835x0w3vtvaga95c09" - }, - { - txid: "61e71554a3dc18158f30d9e8f5c9b6641a789690b32302899f81cbea9fe3bb49", - vout: 1, - sequence: 4294967295, - n: 1, - scriptSig: { - hex: - "483045022100a743bee56c99bd103be48a78fa4c7342100815d9d2448dbe6e1d338c3a13b241022066728b5279fc22eef5cd019582ff34771e29175835fc98aa3168a1548fd78ac8412103c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585", - asm: - "3045022100a743bee56c99bd103be48a78fa4c7342100815d9d2448dbe6e1d338c3a13b241022066728b5279fc22eef5cd019582ff34771e29175835fc98aa3168a1548fd78ac8[ALL|FORKID] 03c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585" - }, - value: 546, - legacyAddress: "mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL", - cashAddress: "bchtest:qz4qnxcxwvmacgye8wlakhz0835x0w3vtvaga95c09" - }, - { - txid: "61e71554a3dc18158f30d9e8f5c9b6641a789690b32302899f81cbea9fe3bb49", - vout: 3, - sequence: 4294967295, - n: 2, - scriptSig: { - hex: - "483045022100821473902eec5f1ce7d43b1ba7f9ec453bfe8b8dfc3de3e0723c883ab109922f02206162960e80618531fab2c16aee260fddd7979bab62471c8686af7de75f8732ec412103c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585", - asm: - "3045022100821473902eec5f1ce7d43b1ba7f9ec453bfe8b8dfc3de3e0723c883ab109922f02206162960e80618531fab2c16aee260fddd7979bab62471c8686af7de75f8732ec[ALL|FORKID] 03c87f0ec048a0771bdb60533d45cac88c6974afeb055a65edd663c2f947335585" - }, - value: 9997521, - legacyAddress: "mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL", - cashAddress: "bchtest:qz4qnxcxwvmacgye8wlakhz0835x0w3vtvaga95c09" - } - ], - vout: [ - { - value: "0.00000000", - n: 0, - scriptPubKey: { - hex: - "6a04534c500001010453454e44207ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796080000000049504f80080000001c71e64280", - asm: - "OP_RETURN 5262419 1 1145980243 7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796 0000000049504f80 0000001c71e64280" - }, - spentTxId: null, - spentIndex: null, - spentHeight: null - }, - { - value: "0.00000546", - n: 1, - scriptPubKey: { - hex: "76a914396b8e57ad0cb58d30e2992f22047b3c20377aa688ac", - asm: - "OP_DUP OP_HASH160 396b8e57ad0cb58d30e2992f22047b3c20377aa6 OP_EQUALVERIFY OP_CHECKSIG", - addresses: ["mkkZf7T3fU3vHSzNPy51HBmM46ghN1gnN9"], - type: "pubkeyhash" - }, - spentTxId: null, - spentIndex: null, - spentHeight: null - }, - { - value: "0.00000546", - n: 2, - scriptPubKey: { - hex: "76a914aa099b067337dc20993bbfdb5c4f3c6867ba2c5b88ac", - asm: - "OP_DUP OP_HASH160 aa099b067337dc20993bbfdb5c4f3c6867ba2c5b OP_EQUALVERIFY OP_CHECKSIG", - addresses: ["mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL"], - type: "pubkeyhash" - }, - spentTxId: null, - spentIndex: null, - spentHeight: null - }, - { - value: "0.09996891", - n: 3, - scriptPubKey: { - hex: "76a914aa099b067337dc20993bbfdb5c4f3c6867ba2c5b88ac", - asm: - "OP_DUP OP_HASH160 aa099b067337dc20993bbfdb5c4f3c6867ba2c5b OP_EQUALVERIFY OP_CHECKSIG", - addresses: ["mw22g57T9YA7MZQQu5eBDKj3PKTdt99oDL"], - type: "pubkeyhash" - }, - spentTxId: null, - spentIndex: null, - spentHeight: null - } - ], - blockhash: "000000000000ce978accc64a6bb567acf0c653c202309a0f8e220149bf0c6968", - blockheight: 1287490, - confirmations: 746, - time: 1550855104, - blocktime: 1550855104, - valueOut: 0.09997983, - size: 628, - valueIn: 0.09998613, - fees: 0.0000063, - tokenInfo: { - versionType: 1, - transactionType: "SEND", - tokenIdHex: - "7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796", - sendOutputs: ["0", "1230000000", "122170000000"] - }, - tokenIsValid: true -} - -const mockConvert = { - slpAddress: "slptest:qz35h5mfa8w2pqma2jq06lp7dnv5fxkp2shlcycvd5", - cashAddress: "bchtest:qz35h5mfa8w2pqma2jq06lp7dnv5fxkp2svtllzmlf", - legacyAddress: "mvQPGnzRT6gMWASZBMg7NcT3vmvsSKSQtf" -} - -const mockTokenDetails = { - tokenIdHex: - "df808a41672a0a0ae6475b44f272a107bc9961b90f29dc918d71301f24fe92fb", - documentUri: "", - symbol: "NAKAMOTO", - name: "NAKAMOTO", - decimals: 8, - timestamp: "", - containsBaton: true, - versionType: 1 -} - -const mockTokenStats = { - qty_valid_txns_since_genesis: 241, - qty_valid_token_utxos: 151, - qty_valid_token_addresses: 113, - qty_token_circulating_supply: "20995990", - qty_token_burned: "4010", - qty_token_minted: "21000000", - qty_satoshis_locked_up: 81900 -} - -const mockBalance = { - slpAddress: "simpleledger:qp9d8mn8ypryfvea2mev0ggc3wg6plpn4suuaeuss3", - satoshis_balance: 546, - token_balance: "1000" -} - -const mockTransactions = [ - { - txid: "a302f045be8efa1cd982833a7f187ff4fac8baac36da0c887eb2787d8b45e2af", - tokenDetails: { - valid: true, - detail: { - decimals: null, - tokenIdHex: - "495322b37d6b2eae81f045eda612b95870a0c2b6069c58f70cf8ef4e6a9fd43a", - timestamp: null, - transactionType: "MINT", - versionType: 1, - documentUri: null, - documentSha256Hex: null, - symbol: null, - name: null, - batonVout: 2, - containsBaton: true, - genesisOrMintQuantity: { - $numberDecimal: "1000" - }, - sendOutputs: null - }, - invalidReason: null, - schema_version: 30 - } - } -] - -const mockFoobar = { - c: [], - u: [] -} - -module.exports = { - mockList, - mockSingleToken, - mockConvert, - mockTokenDetails, - mockTokenStats, - mockTx, - mockBalance, - mockTransactions, - mockSingleTokenError, - mockSingleAddress, - mockFoobar -} diff --git a/test/v2/mocks/slpjs-mocks.js b/test/v2/mocks/slpjs-mocks.js deleted file mode 100644 index c130935..0000000 --- a/test/v2/mocks/slpjs-mocks.js +++ /dev/null @@ -1,71 +0,0 @@ -/* - Mocks used for unit tests that interact with slpjs. -*/ - -"use strict" - -const sinon = require("sinon") -const proxyquire = require("proxyquire") -const BigNumber = require("bignumber.js") -const slpMocks = require("./slp-mocks") - -// Mock the BitboxNetwork class. -class BitboxNetwork { - constructor() {} - - async getAllSlpBalancesAndUtxos(address) { - return { - satoshis_available_bch: 9996891, - satoshis_in_slp_baton: 546, - satoshis_in_slp_token: 546, - satoshis_in_invalid_token_dag: 0, - satoshis_in_invalid_baton_dag: 0, - slpTokenBalances: { - "7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796": new BigNumber( - 123400000000 - ) - }, - slpTokenUtxos: { - "7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796": [] - }, - slpBatonUtxos: { - "7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796": [] - }, - nonSlpUtxos: [{}], - invalidTokenUtxos: [], - invalidBatonUtxos: [] - } - } - - async getTokenInformation(txid) { - BigNumber.set({ DECIMAL_PLACES: 8, ROUNDING_MODE: 4 }) - - const obj = { - versionType: 1, - transactionType: 0, - symbol: "SLPSDK", - name: "SLP SDK example using BITBOX", - documentUri: "developer.bitcoin.com", - documentSha256: null, - decimals: 8, - batonVout: 2, - containsBaton: true, - genesisOrMintQuantity: new BigNumber(123400000000) - } - - return obj - } - - async getTransactionDetails(txid) { - return slpMocks.mockTx - } -} - -// Mock the slpjs library. -const slpjs = { - BitboxNetwork, - slp: {}, - validator: {} -} - -module.exports = slpjs diff --git a/test/v2/mocks/transaction-mocks.js b/test/v2/mocks/transaction-mocks.js deleted file mode 100644 index a8d22e2..0000000 --- a/test/v2/mocks/transaction-mocks.js +++ /dev/null @@ -1,74 +0,0 @@ -/* - This library contains mocking data for running unit tests on the address route. -*/ - -"use strict" - -/* -const mockDetails = { - txid: "6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40", - version: 2, - locktime: 0, - vin: [Array], - vout: [Array], - blockhash: "00000000e7232ff12462dedf9c11985f5b54202515277c337ccc59812758f28b", - blockheight: 1270188, - confirmations: 2, - time: 1543436253, - blocktime: 1543436253, - valueOut: 450.78867333, - size: 226, - valueIn: 450.78867559, - fees: 0.00000226 -} -*/ - -const mockDetails = { - txid: "6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40", - version: 2, - locktime: 0, - vin: [ - { - txid: "273d616d1c48f4b075c497f36ffdc79da5c8d6ed75485808b3599aac504f8525", - vout: 0, - sequence: 4294967295, - n: 0, - scriptSig: [{}], - addr: "bchtest:qqmd9unmhkpx4pkmr6fkrr8rm6y77vckjvqe8aey35", - valueSat: 45078867559, - value: 450.78867559, - doubleSpentTxID: null - } - ], - vout: [ - { - value: "450.68867333", - n: 0, - scriptPubKey: [{}], - spentTxId: null, - spentIndex: null, - spentHeight: null - }, - { - value: "0.10000000", - n: 1, - scriptPubKey: [{}], - spentTxId: null, - spentIndex: null, - spentHeight: null - } - ], - blockhash: "00000000e7232ff12462dedf9c11985f5b54202515277c337ccc59812758f28b", - blockheight: 1270188, - confirmations: 3, - time: 1543436253, - blocktime: 1543436253, - valueOut: 450.78867333, - size: 226, - valueIn: 450.78867559, - fees: 0.00000226 -} - -module.exports = { - mockDetails -} diff --git a/test/v2/mocks/util-mocks.js b/test/v2/mocks/util-mocks.js deleted file mode 100644 index 4118358..0000000 --- a/test/v2/mocks/util-mocks.js +++ /dev/null @@ -1,18 +0,0 @@ -/* - This library contains mocking data for running unit tests on the address route. -*/ - -"use strict" - -const mockAddress = { - isvalid: true, - address: "bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y", - scriptPubKey: "76a914016a935f87e8deeab04501889b22d87e4e52db0988ac", - ismine: false, - iswatchonly: false, - isscript: false -} - -module.exports = { - mockAddress -} diff --git a/test/v2/rate-limits.js b/test/v2/rate-limits.js deleted file mode 100644 index 030fa64..0000000 --- a/test/v2/rate-limits.js +++ /dev/null @@ -1,166 +0,0 @@ -"use strict" - -const chai = require("chai") -const assert = chai.assert -const nock = require("nock") // HTTP mocking - -// Used for debugging. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -// Mocking data. -const { mockReq, mockRes, mockNext } = require("./mocks/express-mocks") - -// Libraries under test -const rateLimitMiddleware = require("../../src/middleware/route-ratelimit") -const controlRoute = require("../../src/routes/v2/control") - -let req, res, next -let originalEnvVars // Used during transition from integration to unit tests. - -describe("#route-ratelimits", () => { - before(() => { - // Save existing environment variables. - originalEnvVars = { - BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL, - RPC_BASEURL: process.env.RPC_BASEURL, - RPC_USERNAME: process.env.RPC_USERNAME, - RPC_PASSWORD: process.env.RPC_PASSWORD - } - }) - - // Setup the mocks before each test. - beforeEach(() => { - // Mock the req and res objects used by Express routes. - req = mockReq - res = mockRes - next = mockNext - - // Explicitly reset the parmas and body. - req.params = {} - req.body = {} - req.query = {} - }) - - describe("#routeRateLimit", () => { - const routeRateLimit = rateLimitMiddleware.routeRateLimit - const getInfo = controlRoute.testableComponents.getInfo - /* - it("should pass through rate-limit middleware", async () => { - req.baseUrl = "/v2" - req.path = "/control/getInfo" - req.method = "GET" - - await routeRateLimit(req, res, next) - - // next() will be called if rate-limit is not triggered - assert.equal(next.called, true) - }) - - it("should trigger rate-limit handler if rate limits exceeds 60 request per minute", async () => { - req.baseUrl = "/v2" - req.path = "/control/getInfo" - req.method = "GET" - - for (let i = 0; i < 65; i++) { - next.reset() // reset the stubbed next() function. - - await routeRateLimit(req, res, next) - //console.log(`next() called: ${next.called}`) - } - - // Note: next() will be called unless the rate-limit kicks in. - assert.equal( - next.called, - false, - `next should not be called if rate limit was triggered.` - ) - }) - */ - /* - it("should NOT trigger rate-limit handler for pro-tier at 65 RPM", async () => { - // Clear the require cache before running this test. - delete require.cache[ - require.resolve("../../dist/middleware/route-ratelimit") - ] - rateLimitMiddleware = require("../../dist/middleware/route-ratelimit") - routeRateLimit = rateLimitMiddleware.routeRateLimit - - req.baseUrl = "/v2" - req.path = "/control/getInfo" - req.method = "GET" - - req.locals.proLimit = true - - //console.log(`req.locals before test: ${util.inspect(req.locals)}`) - - // Prepare the authorization header - //req.headers.authorization = generateAuthHeader("BITBOX") - - for (let i = 0; i < 65; i++) { - next.reset() // reset the stubbed next() function. - - await routeRateLimit(req, res, next) - //console.log(`next() called: ${next.called}`) - } - - //console.log(`req.locals after test: ${util.inspect(req.locals)}`) - - // Note: next() will be called unless the rate-limit kicks in. - assert.equal( - next.called, - true, - `next should be called if rate limit was not triggered.` - ) - }) - - it("rate-limiting should still kick in at a higher RPM for pro-tier", async () => { - // Clear the require cache before running this test. - delete require.cache[ - require.resolve("../../dist/middleware/route-ratelimit") - ] - rateLimitMiddleware = require("../../dist/middleware/route-ratelimit") - routeRateLimit = rateLimitMiddleware.routeRateLimit - - req.baseUrl = "/v2" - req.path = "/control/getInfo" - req.method = "GET" - - req.locals.proLimit = true - - //console.log(`req.locals before test: ${util.inspect(req.locals)}`) - - // Prepare the authorization header - //req.headers.authorization = generateAuthHeader("BITBOX") - - for (let i = 0; i < 650; i++) { - next.reset() // reset the stubbed next() function. - - await routeRateLimit(req, res, next) - //console.log(`next() called: ${next.called}`) - } - - //console.log(`req.locals after test: ${util.inspect(req.locals)}`) - - // Note: next() will be called unless the rate-limit kicks in. - assert.equal( - next.called, - false, - `next should NOT be called if rate limit was triggered.` - ) - }) - */ - }) -}) - -// Generates a Basic authorization header. -function generateAuthHeader(pass) { - // https://en.wikipedia.org/wiki/Basic_access_authentication - const username = "BITBOX" - const combined = `${username}:${pass}` - - var base64Credential = Buffer.from(combined).toString("base64") - var readyCredential = `Basic ${base64Credential}` - - return readyCredential -} diff --git a/test/v2/raw-transactions.js b/test/v2/raw-transactions.js deleted file mode 100644 index bd20bcc..0000000 --- a/test/v2/raw-transactions.js +++ /dev/null @@ -1,807 +0,0 @@ -/* - TESTS FOR THE RAWTRANSACTIONS.TS LIBRARY - - This test file uses the environment variable TEST to switch between unit - and integration tests. By default, TEST is set to 'unit'. Set this variable - to 'integration' to run the tests against BCH mainnet. - - TODO: - -Create e2e test for sendRawTransaction. - -*/ - -"use strict" - -const chai = require("chai") -const assert = chai.assert -const rawtransactions = require("../../src/routes/v2/rawtransactions") -const nock = require("nock") // HTTP mocking - -let originalEnvVars // Used during transition from integration to unit tests. - -// Mocking data. -//delete require.cache[require.resolve("./mocks/express-mocks")] // Fixes bug -const { mockReq, mockRes, mockNext } = require("./mocks/express-mocks") -const mockData = require("./mocks/raw-transactions-mocks") - -// Used for debugging. -const util = require("util") -util.inspect.defaultOptions = { depth: 5 } - -describe("#Raw-Transactions", () => { - let req, res, next - - before(() => { - // Save existing environment variables. - originalEnvVars = { - BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL, - RPC_BASEURL: process.env.RPC_BASEURL, - RPC_USERNAME: process.env.RPC_USERNAME, - RPC_PASSWORD: process.env.RPC_PASSWORD - } - - // Set default environment variables for unit tests. - if (!process.env.TEST) process.env.TEST = "unit" - if (process.env.TEST === "unit") { - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - process.env.RPC_BASEURL = "http://fakeurl/api" - process.env.RPC_USERNAME = "fakeusername" - process.env.RPC_PASSWORD = "fakepassword" - } - }) - - // Setup the mocks before each test. - beforeEach(() => { - // Mock the req and res objects used by Express routes. - req = mockReq - res = mockRes - next = mockNext - - // Explicitly reset the parmas and body. - req.params = {} - req.body = {} - req.query = {} - req.locals = {} - - // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() - }) - - afterEach(() => { - // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() - }) - - after(() => { - // Restore any pre-existing environment variables. - process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL - process.env.RPC_BASEURL = originalEnvVars.RPC_BASEURL - process.env.RPC_USERNAME = originalEnvVars.RPC_USERNAME - process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD - }) - - describe("#root", async () => { - // root route handler. - const root = rawtransactions.testableComponents.root - - it("should respond to GET for base route", async () => { - const result = root(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(result.status, "rawtransactions", "Returns static string") - }) - }) - - describe("decodeRawTransactionSingle()", () => { - // block route handler. - const decodeRawTransaction = - rawtransactions.testableComponents.decodeRawTransactionSingle - - it("should throw error if hex is missing", async () => { - const result = await decodeRawTransaction(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "hex can not be empty") - }) - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - req.params.hex = - "0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" - - const result = await decodeRawTransaction(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or greater expected." - ) - //assert.include(result.error,"Network error: Could not communicate with full node","Error message expected") - }) - - it("should GET /decodeRawTransaction", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockDecodeRawTransaction }) - } - - req.params.hex = - "0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" - - const result = await decodeRawTransaction(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAnyKeys(result, [ - "txid", - "hash", - "size", - "version", - "locktime", - "vin", - "vout" - ]) - assert.isArray(result.vin) - assert.isArray(result.vout) - }) - }) - - describe("decodeRawTransactionBulk()", () => { - const decodeRawTransactionBulk = - rawtransactions.testableComponents.decodeRawTransactionBulk - - it("should throw 400 error if hexes array is missing", async () => { - const result = await decodeRawTransactionBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "hexes must be an array") - }) - - it("should throw 400 error if hexes array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.hexes = testArray - - const result = await decodeRawTransactionBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should throw 400 error if hexes is empty", async () => { - req.body.hexes = [""] - - const result = await decodeRawTransactionBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Encountered empty hex") - }) - - it("should error on non-array single hex", async () => { - req.body.hexes = - "0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" - - const result = await decodeRawTransactionBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "hexes must be an array", - "Proper error message" - ) - }) - - it("should decode an array with a single hex", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockDecodeRawTransaction }) - } - - req.body.hexes = [ - "0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" - ] - - const result = await decodeRawTransactionBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAnyKeys(result[0], [ - "txid", - "hash", - "size", - "version", - "locktime", - "vin", - "vout" - ]) - assert.isArray(result[0].vin) - assert.isArray(result[0].vout) - }) - - it("should decode an array with multiple hexes", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .times(2) - .reply(200, { result: mockData.mockDecodeRawTransaction }) - } - - req.body.hexes = [ - "0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000", - "0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" - ] - - const result = await decodeRawTransactionBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAnyKeys(result[0], [ - "txid", - "hash", - "size", - "version", - "locktime", - "vin", - "vout" - ]) - assert.isArray(result[0].vin) - assert.isArray(result[0].vout) - }) - }) - - describe("decodeScriptSingle()", () => { - // block route handler. - const decodeScriptSingle = - rawtransactions.testableComponents.decodeScriptSingle - - it("should throw error if hex is missing", async () => { - const result = await decodeScriptSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "hex can not be empty") - }) - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - req.params.hex = - "0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" - - const result = await decodeScriptSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or greater expected." - ) - //assert.include(result.error,"Network error: Could not communicate with full node","Error message expected") - }) - - it("should GET /decodeScriptSingle", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockDecodeScript }) - } - - req.params.hex = - "0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" - - const result = await decodeScriptSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["asm", "type", "p2sh"]) - }) - }) - - describe("decodeScriptBulk()", () => { - const decodeScriptBulk = rawtransactions.testableComponents.decodeScriptBulk - - it("should throw 400 error if hexes array is missing", async () => { - const result = await decodeScriptBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "hexes must be an array") - }) - - it("should throw 400 error if hexes array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.hexes = testArray - - const result = await decodeScriptBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should throw 400 error if hexes is empty", async () => { - req.body.hexes = [""] - - const result = await decodeScriptBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Encountered empty hex") - }) - - it("should error on non-array single hex", async () => { - req.body.hexes = - "0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000" - - const result = await decodeScriptBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "hexes must be an array", - "Proper error message" - ) - }) - - it("should decode an array with a single hex", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockDecodeScript }) - } - - req.body.hexes = [ - "4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16" - ] - - const result = await decodeScriptBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAllKeys(result[0], ["asm", "type", "p2sh"]) - }) - - it("should decode an array with a multiple hexes", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .times(2) - .reply(200, { result: mockData.mockDecodeScript }) - } - - req.body.hexes = [ - "4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16", - "4830450221009a51e00ec3524a7389592bc27bea4af5104a59510f5f0cfafa64bbd5c164ca2e02206c2a8bbb47eabdeed52f17d7df668d521600286406930426e3a9415fe10ed592012102e6e1423f7abde8b70bca3e78a7d030e5efabd3eb35c19302542b5fe7879c1a16" - ] - - const result = await decodeScriptBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.equal(result.length, 2) - assert.hasAllKeys(result[0], ["asm", "type", "p2sh"]) - }) - }) - - describe("getRawTransactionBulk()", () => { - // block route handler. - const getRawTransactionBulk = - rawtransactions.testableComponents.getRawTransactionBulk - - it("should throw 400 error if txids array is missing", async () => { - const result = await getRawTransactionBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "txids must be an array") - }) - - it("should throw 400 error if txids array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.txids = testArray - - const result = await getRawTransactionBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should throw 400 error if txid is empty", async () => { - req.body.txids = [""] - - const result = await getRawTransactionBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Encountered empty TXID") - }) - - it("should throw 400 error if txid is invalid", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(500, { - error: { message: "parameter 1 must be of length 64 (not 6)" } - }) - } - - req.body.txids = ["abc123"] - - const result = await getRawTransactionBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "parameter 1 must be of length 64 (not 6)") - }) - - it("should get concise transaction data", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockRawTransactionConcise }) - } - - req.body.txids = [ - "bd320377db7026a3dd5c7ec444596c0ee18fc25c4f34ee944adc03e432ce1971" - ] - - const result = await getRawTransactionBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.isString(result[0]) - }) - - it("should get verbose transaction data", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockRawTransactionVerbose }) - } - - req.body.txids = [ - "bd320377db7026a3dd5c7ec444596c0ee18fc25c4f34ee944adc03e432ce1971" - ] - req.body.verbose = true - - const result = await getRawTransactionBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAnyKeys(result[0], [ - "hex", - "txid", - "hash", - "size", - "version", - "locktime", - "vin", - "vout", - "blockhash", - "confirmations", - "time", - "blocktime" - ]) - assert.isArray(result[0].vin) - assert.isArray(result[0].vout) - }) - }) - - describe("getRawTransactionSingle()", () => { - // block route handler. - const getRawTransactionSingle = - rawtransactions.testableComponents.getRawTransactionSingle - - it("should throw 400 error if txid is missing", async () => { - const result = await getRawTransactionSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "txid can not be empty") - }) - - it("should throw 400 error if txid is invalid", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(500, { - error: { message: "parameter 1 must be of length 64 (not 6)" } - }) - } - - req.params.txid = "abc123" - - const result = await getRawTransactionSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "parameter 1 must be of length 64 (not 6)") - }) - - it("should get concise transaction data", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockRawTransactionConcise }) - } - - req.params.txid = - "bd320377db7026a3dd5c7ec444596c0ee18fc25c4f34ee944adc03e432ce1971" - - const result = await getRawTransactionSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isString(result) - }) - - it("should get verbose transaction data", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockRawTransactionVerbose }) - } - - req.params.txid = - "bd320377db7026a3dd5c7ec444596c0ee18fc25c4f34ee944adc03e432ce1971" - req.query.verbose = "true" - - const result = await getRawTransactionSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAnyKeys(result, [ - "hex", - "txid", - "hash", - "size", - "version", - "locktime", - "vin", - "vout", - "blockhash", - "confirmations", - "time", - "blocktime" - ]) - assert.isArray(result.vin) - assert.isArray(result.vout) - }) - }) - - describe("sendRawTransactionBulk()", () => { - const sendRawTransaction = - rawtransactions.testableComponents.sendRawTransactionBulk - - it("should throw 400 error if hexs array is missing", async () => { - const result = await sendRawTransaction(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "hex must be an array") - }) - - it("should throw 400 error if hexs array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.hexes = testArray - - const result = await sendRawTransaction(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should throw 400 error if hex array element is empty", async () => { - req.body.hexes = [""] - - const result = await sendRawTransaction(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Encountered empty hex") - }) - - it("should throw 500 error if hex is invalid", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(500, { - error: { message: "TX decode failed" } - }) - } - - req.body.hexes = ["abc123"] - - const result = await sendRawTransaction(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "TX decode failed") - }) - - it("should submit hex encoded transaction", async () => { - // This is a difficult test to run as transaction hex is invalid after a - // block confirmation. So the unit tests simulates what the output 'should' - // be, but the integration asserts an expected failure. - - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { - result: - "aef8848396e67532b42008b9d75b5a5a3459a6717740f31f0553b74102b4b118" - }) - } - - req.body.hexes = [ - "0200000001189f7cf4303e2e0bcc5af4be323b9b397dd4104ca2de09528eb90a1450b8a999010000006a4730440220212ec2ffce136a30cec1bc86a40b08a2afdeb6f8dbd652d7bcb07b1aad6dfa8c022041f59585273b89d88879a9a531ba3272dc953f48ff57dad955b2dee70e76c0624121030143ffd18f1c4add75c86b2f930d9551d51f7a6bd786314247022b7afc45d231ffffffff0230d39700000000001976a914af64a026e06910c59463b000d18c3d125d7e951a88ac58c20000000000001976a914af64a026e06910c59463b000d18c3d125d7e951a88ac00000000" - ] - - const result = await sendRawTransaction(req, res) - //console.log(`result: ${util.inspect(result)}`) - - if (process.env.TEST === "unit") { - assert.isArray(result) - assert.isString(result[0]) - - // Integration test - } else { - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "transaction already in block chain") - } - }) - }) - - describe("sendRawTransactionSingle()", () => { - // block route handler. - const sendRawTransaction = - rawtransactions.testableComponents.sendRawTransactionSingle - - it("should throw an error for an empty hex", async () => { - req.params.hex = "" - - const result = await sendRawTransaction(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Encountered empty hex", - "Proper error message" - ) - }) - - it("should throw an error for a non-string", async () => { - req.params.hex = 456 - - const result = await sendRawTransaction(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "hex must be a string", - "Proper error message" - ) - }) - - it("should throw 500 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl = process.env.BITCOINCOM_BASEURL - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - process.env.RPC_BASEURL = "http://fakeurl/api/" - - req.params.hex = - "0200000001189f7cf4303e2e0bcc5af4be323b9b397dd4104ca2de09528eb90a1450b8a999010000006a4730440220212ec2ffce136a30cec1bc86a40b08a2afdeb6f8dbd652d7bcb07b1aad6dfa8c022041f59585273b89d88879a9a531ba3272dc953f48ff57dad955b2dee70e76c0624121030143ffd18f1c4add75c86b2f930d9551d51f7a6bd786314247022b7afc45d231ffffffff0230d39700000000001976a914af64a026e06910c59463b000d18c3d125d7e951a88ac58c20000000000001976a914af64a026e06910c59463b000d18c3d125d7e951a88ac00000000" - const result = await sendRawTransaction(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - process.env.RPC_BASEURL = savedUrl2 - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or great expected." - ) - }) - - it("should throw an error for invalid hex", async () => { - req.params.hex = "abc123" - - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(500, { - error: { message: "TX decode failed" } - }) - } - - const result = await sendRawTransaction(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "TX decode failed") - }) - - it("should GET /sendRawTransaction/:hex", async () => { - // This is a difficult test to run as transaction hex is invalid after a - // block confirmation. So the unit tests simulates what the output 'should' - // be, but the integration asserts an expected failure. - - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { - result: - "aef8848396e67532b42008b9d75b5a5a3459a6717740f31f0553b74102b4b118" - }) - } - - req.params.hex = - "0200000001189f7cf4303e2e0bcc5af4be323b9b397dd4104ca2de09528eb90a1450b8a999010000006a4730440220212ec2ffce136a30cec1bc86a40b08a2afdeb6f8dbd652d7bcb07b1aad6dfa8c022041f59585273b89d88879a9a531ba3272dc953f48ff57dad955b2dee70e76c0624121030143ffd18f1c4add75c86b2f930d9551d51f7a6bd786314247022b7afc45d231ffffffff0230d39700000000001976a914af64a026e06910c59463b000d18c3d125d7e951a88ac58c20000000000001976a914af64a026e06910c59463b000d18c3d125d7e951a88ac00000000" - - const result = await sendRawTransaction(req, res) - //console.log(`result: ${util.inspect(result)}`) - - if (process.env.TEST === "unit") { - assert.isString(result) - - // Integration test - } else { - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "transaction already in block chain") - } - }) - }) -}) diff --git a/test/v2/slp.js b/test/v2/slp.js deleted file mode 100644 index 2049a77..0000000 --- a/test/v2/slp.js +++ /dev/null @@ -1,971 +0,0 @@ -/* - TESTS FOR THE SLP.TS LIBRARY - - This test file uses the environment variable TEST to switch between unit - and integration tests. By default, TEST is set to 'unit'. Set this variable - to 'integration' to run the tests against BCH mainnet. - - TODO: - -See listSingleToken() tests. -*/ - -"use strict" - -const chai = require("chai") -const assert = chai.assert -const nock = require("nock") // HTTP mocking -const sinon = require("sinon") -const proxyquire = require("proxyquire").noPreserveCache() - -// Prepare the slpRoute for stubbing dependcies on slpjs. -const slpRoute = require("../../src/routes/v2/slp") -const pathStub = {} // Used to stub methods within slpjs. -const slpRouteStub = proxyquire("../../src/routes/v2/slp", { slpjs: pathStub }) - -let originalEnvVars // Used during transition from integration to unit tests. - -// Mocking data. -const { mockReq, mockRes } = require("./mocks/express-mocks") -const mockData = require("./mocks/slp-mocks") -const slpjsMock = require("./mocks/slpjs-mocks") - -// Used for debugging. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -describe("#SLP", () => { - let req, res, mockServerUrl - let sandbox - - before(() => { - // Save existing environment variables. - originalEnvVars = { - BITDB_URL: process.env.BITDB_URL, - BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL, - SLPDB_URL: process.env.SLPDB_URL - } - - // Set default environment variables for unit tests. - if (!process.env.TEST) process.env.TEST = "unit" - - // Block network connections for unit tests. - if (process.env.TEST === "unit") { - process.env.BITDB_URL = "http://fakeurl/" - process.env.BITCOINCOM_BASEURL = "http://fakeurl/" - process.env.SLPDB_URL = "http://fakeurl/" - mockServerUrl = `http://fakeurl` - } - }) - - // Setup the mocks before each test. - beforeEach(() => { - // Mock the req and res objects used by Express routes. - req = mockReq - res = mockRes - - // Explicitly reset the parmas and body. - //req.params = {} - req.body = {} - req.query = {} - req.locals = {} - - // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() - - sandbox = sinon.createSandbox() - }) - - afterEach(() => { - // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() - - sandbox.restore() - }) - - after(() => { - // Restore any pre-existing environment variables. - process.env.BITDB_URL = originalEnvVars.BITDB_URL - process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL - process.env.SLPDB_URL = originalEnvVars.SLPDB_URL - }) - - describe("#root", async () => { - // root route handler. - const root = slpRoute.testableComponents.root - - it("should respond to GET for base route", async () => { - const result = root(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(result.status, "slp", "Returns static string") - }) - }) - - describe("list()", () => { - // list route handler - const list = slpRoute.testableComponents.list - - it("should throw 500 when network issues", async () => { - // Save the existing SLPDB_URL. - const savedUrl2 = process.env.SLPDB_URL - - // Manipulate the URL to cause a 500 network error. - process.env.SLPDB_URL = "http://fakeurl/api/" - - const result = await list(req, res) - // console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.SLPDB_URL = savedUrl2 - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or greater expected." - ) - //assert.include(result.error,"Network error: Could not communicate with full node","Error message expected") - }) - - it("should GET list", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - const b64 = `eyJ2IjozLCJxIjp7ImRiIjpbInQiXSwiZmluZCI6eyIkcXVlcnkiOnt9fSwicHJvamVjdCI6eyJ0b2tlbkRldGFpbHMiOjEsInRva2VuU3RhdHMiOjEsIl9pZCI6MH0sImxpbWl0IjoxMDB9fQ==` - - nock(process.env.SLPDB_URL) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockList) - } - - const result = await list(req, res) - // console.log(`test result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAnyKeys(result[0], [ - "id", - "timestamp", - "symbol", - "name", - "documentUri", - "documentHash", - "decimals", - "initialTokenQty" - ]) - }) - }) - - describe("listSingleToken()", () => { - const listSingleToken = slpRoute.testableComponents.listSingleToken - - it("should throw 400 if tokenId is empty", async () => { - const result = await listSingleToken(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "tokenId can not be empty") - }) - - it("should throw 503 when network issues", async () => { - // Save the existing BITDB_URL. - const savedUrl2 = process.env.SLPDB_URL - - // Manipulate the URL to cause a 500 network error. - process.env.SLPDB_URL = "http://fakeurl/api/" - - req.params.tokenId = - "650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a" - - const result = await listSingleToken(req, res) - // console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.SLPDB_URL = savedUrl2 - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or greater expected." - ) - //assert.include(result.error,"Network error: Could not communicate with full node","Error message expected") - }) - - it("should return 'not found' for mainnet txid on testnet", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(mockServerUrl) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockSingleToken) - } - - req.params.tokenId = - // testnet - //"650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a" - // mainnet - "259908ae44f46ef585edef4bcc1e50dc06e4c391ac4be929fae27235b8158cf1" - - const result = await listSingleToken(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["id"]) - assert.include(result.id, "not found") - }) - - it("should get token information", async () => { - // testnet - const tokenIdToTest = - "650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a" - - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(mockServerUrl) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockSingleToken) - } - - req.params.tokenId = tokenIdToTest - - const result = await listSingleToken(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, [ - "id", - "blockCreated", - "blockLastActiveMint", - "blockLastActiveSend", - "circulatingSupply", - "containsBaton", - "mintingBatonStatus", - "txnsSinceGenesis", - "versionType", - "timestamp", - "symbol", - "name", - "documentUri", - "documentHash", - "decimals", - "initialTokenQty", - "totalBurned", - "totalMinted", - "validAddresses" - ]) - }) - }) - - describe("listBulkToken()", () => { - const listBulkToken = slpRoute.testableComponents.listBulkToken - - it("should throw 400 if tokenIds array is empty", async () => { - const result = await listBulkToken(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "tokenIds needs to be an array") - assert.equal(res.statusCode, 400) - }) - - it("should throw 400 error if array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.tokenIds = testArray - - const result = await listBulkToken(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should throw 400 if tokenId is empty", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(mockServerUrl) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockEmptyTokenId) - } - req.body.tokenIds = "" - - const result = await listBulkToken(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include( - result.error, - "tokenIds needs to be an array. Use GET for single tokenId." - ) - }) - - it("should throw 503 when network issues", async () => { - // Save the existing BITDB_URL. - const savedUrl2 = process.env.SLPDB_URL - - // Manipulate the URL to cause a 500 network error. - process.env.SLPDB_URL = "http://fakeurl/api/" - - req.body.tokenIds = [ - "650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a" - ] - - const result = await listBulkToken(req, res) - // console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.SLPDB_URL = savedUrl2 - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or greater expected." - ) - //assert.include(result.error,"Network error: Could not communicate with full node","Error message expected") - }) - - it("should return 'not found' for mainnet txid on testnet", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(mockServerUrl) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockSingleTokenError) - } - - req.body.tokenIds = - // testnet - //"650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a" - // mainnet - ["0b314bc2b2905b8844222871c6b665ae3494117c83b11302824561bb904efb6b"] - - const result = await listBulkToken(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAllKeys(result[0], ["id", "valid"]) - assert.strictEqual(result[0].valid, false) - }) - - it("should get token information for single token ID", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(mockServerUrl) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockSingleToken) - } - - req.body.tokenIds = - // testnet - ["650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a"] - - const result = await listBulkToken(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAllKeys(result[0], [ - "blockCreated", - "blockLastActiveMint", - "blockLastActiveSend", - "circulatingSupply", - "containsBaton", - "mintingBatonStatus", - "txnsSinceGenesis", - "versionType", - "timestamp", - "symbol", - "name", - "documentUri", - "documentHash", - "decimals", - "initialTokenQty", - "id", - "totalBurned", - "totalMinted", - "validAddresses" - ]) - }) - - it("should get token information for multiple token IDs", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(mockServerUrl) - .get(uri => uri.includes("/")) - .times(2) - .reply(200, mockData.mockSingleToken) - } - - req.body.tokenIds = - // testnet - [ - "650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a", - "c35a87afad11c8d086c1449ffd8b0a84324e72b15b1bcfdf166a493551b4eea6" - ] - - const result = await listBulkToken(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAllKeys(result[0], [ - "blockCreated", - "blockLastActiveMint", - "blockLastActiveSend", - "circulatingSupply", - "containsBaton", - "mintingBatonStatus", - "txnsSinceGenesis", - "versionType", - "timestamp", - "symbol", - "name", - "documentUri", - "documentHash", - "decimals", - "initialTokenQty", - "id", - "totalBurned", - "totalMinted", - "validAddresses" - ]) - }) - }) - - describe("balancesForAddress()", () => { - const balancesForAddress = slpRoute.testableComponents.balancesForAddress - - it("should throw 400 if address is empty", async () => { - const result = await balancesForAddress(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "address can not be empty") - }) - - it("should throw 400 if address is invalid", async () => { - req.params.address = "badAddress" - - const result = await balancesForAddress(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Invalid BCH address.") - }) - - it("should throw 400 if address network mismatch", async () => { - req.params.address = - "simpleledger:qr5agtachyxvrwxu76vzszan5pnvuzy8duhv4lxrsk" - - const result = await balancesForAddress(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Invalid") - }) - - it("should throw 5XX error when network issues", async () => { - // Save the existing SLPDB_URL. - const savedUrl2 = process.env.SLPDB_URL - - // Manipulate the URL to cause a 500 network error. - process.env.SLPDB_URL = "http://fakeurl/api/" - - req.params.address = "slptest:qz35h5mfa8w2pqma2jq06lp7dnv5fxkp2shlcycvd5" - - const result = await balancesForAddress(req, res) - // console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.SLPDB_URL = savedUrl2 - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or greater expected." - ) - assert.include( - result.error, - "Network error: Could not communicate", - "Error message expected" - ) - }) - - it("should get token balance for an address", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(mockServerUrl) - .get(uri => uri.includes("/")) - .times(2) - .reply(200, mockData.mockSingleAddress) - } - - req.params.address = "slptest:pz0qcslrqn7hr44hsszwl4lw5r6udkg6zqv7sq3kk7" - - const result = await balancesForAddress(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAllKeys(result[0], [ - "tokenId", - "balance", - "slpAddress", - "decimalCount" - ]) - }) - }) - - describe("balancesForAddressByTokenID()", () => { - const balancesForAddressByTokenID = - slpRoute.testableComponents.balancesForAddressByTokenID - - it("should throw 400 if address is empty", async () => { - req.params.address = "" - req.params.tokenId = - "650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a" - const result = await balancesForAddressByTokenID(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "address can not be empty") - }) - - it("should throw 400 if tokenId is empty", async () => { - req.params.address = - "simpleledger:qr5agtachyxvrwxu76vzszan5pnvuzy8duhv4lxrsk" - req.params.tokenId = "" - const result = await balancesForAddressByTokenID(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "tokenId can not be empty") - }) - - it("should throw 400 if address is invalid", async () => { - req.params.address = "badAddress" - req.params.tokenId = - "650dea14c77f4d749608e36e375450c9ac91deb8b1b53e50cb0de2059a52d19a" - - const result = await balancesForAddressByTokenID(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Invalid BCH address.") - }) - - it("should throw 400 if address network mismatch", async () => { - req.params.address = - "simpleledger:qr5agtachyxvrwxu76vzszan5pnvuzy8duhv4lxrsk" - - const result = await balancesForAddressByTokenID(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Invalid") - }) - - it("should throw 5XX error when network issues", async () => { - // Save the existing SLPDB_URL. - const savedUrl2 = process.env.SLPDB_URL - - // Manipulate the URL to cause a 500 network error. - process.env.SLPDB_URL = "http://fakeurl/api/" - - req.params.address = "slptest:qz4qnxcxwvmacgye8wlakhz0835x0w3vtvxu67w0ac" - req.params.tokenId = - "7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796" - - const result = await balancesForAddressByTokenID(req, res) - // console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.SLPDB_URL = savedUrl2 - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or greater expected." - ) - assert.include( - result.error, - "Network error: Could not communicate", - "Error message expected" - ) - }) - - it("should get token information", async () => { - if (process.env.TEST === "unit") { - nock(mockServerUrl) - .get(uri => uri.includes("/")) - .times(2) - .reply(200, mockData.mockSingleAddress) - } - - req.params.address = "slptest:pz0qcslrqn7hr44hsszwl4lw5r6udkg6zqv7sq3kk7" - req.params.tokenId = - "6b081fcd1f78b187be1464313dac8ff257251b727a42b613552a4040870aeb29" - - const result = await balancesForAddressByTokenID(req, res) - // console.log(`result: ${util.inspect(result)}`) - - // TODO - add decimalCount - // assert.hasAllKeys(result, ["tokenId", "balance", "decimalCount"]) - assert.hasAllKeys(result, ["tokenId", "balance"]) - }) - }) - - describe("convertAddressSingle()", () => { - const convertAddressSingle = - slpRoute.testableComponents.convertAddressSingle - - it("should throw 400 if address is empty", async () => { - req.params.address = "" - const result = await convertAddressSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "address can not be empty") - }) - // - it("should convert address", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.SLPDB_URL}`) - .post(``) - .reply(200, { result: mockData.mockConvert }) - } - - req.params.address = "slptest:qz35h5mfa8w2pqma2jq06lp7dnv5fxkp2shlcycvd5" - - const result = await convertAddressSingle(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["cashAddress", "legacyAddress", "slpAddress"]) - }) - }) - - describe("convertAddressBulk()", () => { - const convertAddressBulk = slpRoute.testableComponents.convertAddressBulk - - it("should throw 400 if addresses array is empty", async () => { - const result = await convertAddressBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "addresses needs to be an array") - assert.equal(res.statusCode, 400) - }) - - it("should throw 400 error if array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.addresses = testArray - - const result = await convertAddressBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should error on malformed address", async () => { - try { - req.body.addresses = ["bitcoincash:qzs02v05l7qs5s5dwuj0cx5ehjm2c"] - - await convertAddressBulk(req, res) - - assert.equal(true, false, "Unsupported address format") - } catch (err) { - // console.log(`err.message: ${util.inspect(err.message)}`) - - assert.include(err.message, `Unsupported address format`) - } - }) - - it("should validate array with single element", async () => { - req.body.addresses = [ - "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" - ] - - const result = await convertAddressBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAllKeys(result[0], [ - "slpAddress", - "cashAddress", - "legacyAddress" - ]) - }) - - it("should validate array with multiple elements", async () => { - req.body.addresses = [ - "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", - "bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0" - ] - - const result = await convertAddressBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAllKeys(result[0], [ - "slpAddress", - "cashAddress", - "legacyAddress" - ]) - }) - }) - - describe("validateBulk()", () => { - const validateBulk = slpRoute.testableComponents.validateBulk - - it("should throw 400 if txid array is empty", async () => { - const result = await validateBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "txids needs to be an array") - assert.equal(res.statusCode, 400) - }) - - it("should throw 400 error if array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.txids = testArray - - const result = await validateBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should validate array with single element", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - sandbox - .stub(slpRoute.testableComponents, "isValidSlpTxid") - .resolves(true) - } - - req.body.txids = [ - "78d57a82a0dd9930cc17843d9d06677f267777dd6b25055bad0ae43f1b884091" - ] - - const result = await validateBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAllKeys(result[0], ["txid", "valid"]) - }) - - it("should validate array with two elements", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - sandbox - .stub(slpRoute.testableComponents, "isValidSlpTxid") - .resolves(true) - } - - req.body.txids = [ - "78d57a82a0dd9930cc17843d9d06677f267777dd6b25055bad0ae43f1b884091", - "82d996847a861b08b1601284ef7d40a1777d019154a6c4ed11571609dd3555ac" - ] - - const result = await validateBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAllKeys(result[0], ["txid", "valid"]) - assert.equal(result.length, 2) - }) - }) - - describe("tokenStatsSingle()", () => { - const tokenStatsSingle = slpRoute.testableComponents.tokenStats - - it("should throw 400 if tokenID is empty", async () => { - req.params.tokenId = "" - const result = await tokenStatsSingle(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "tokenId can not be empty") - }) - // - it("should get token stats for tokenId", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.SLPDB_URL}`) - .get(uri => uri.includes("/")) - .reply(200, { - t: [ - { - tokenDetails: mockData.mockTokenDetails, - tokenStats: mockData.mockTokenStats - } - ] - }) - } - - req.params.tokenId = - "37279c7dc81ceb34d12f03344b601c582e931e05d0e552c29c428bfa39d39af3" - - const result = await tokenStatsSingle(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, [ - "blockCreated", - "blockLastActiveMint", - "blockLastActiveSend", - "containsBaton", - "initialTokenQty", - "mintingBatonStatus", - "circulatingSupply", - "decimals", - "documentHash", - "versionType", - "timestamp", - "documentUri", - "name", - "symbol", - "id", - "totalBurned", - "totalMinted", - "txnsSinceGenesis", - "validAddresses" - ]) - }) - }) - - describe("balancesForTokenSingle()", () => { - const balancesForTokenSingle = - slpRoute.testableComponents.balancesForTokenSingle - - it("should throw 400 if tokenID is empty", async () => { - req.params.tokenId = "" - const result = await balancesForTokenSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "tokenId can not be empty") - }) - // - it("should get balances for tokenId", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.SLPDB_URL}`) - .get(uri => uri.includes("/")) - .reply(200, { - a: [mockData.mockBalance] - }) - } - - req.params.tokenId = - "37279c7dc81ceb34d12f03344b601c582e931e05d0e552c29c428bfa39d39af3" - - const result = await balancesForTokenSingle(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result[0], ["tokenId", "slpAddress", "tokenBalance"]) - }) - }) - - describe("txDetails()", () => { - let txDetails = slpRoute.testableComponents.txDetails - - it("should throw 400 if txid is empty", async () => { - const result = await txDetails(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "txid can not be empty") - }) - - it("should throw 400 for malformed txid", async () => { - req.params.txid = - "57b3082a2bf269b3d6f40fee7fb9c664e8256a88ca5ee2697c05b9457" - - const result = await txDetails(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "This is not a txid") - }) - - it("should throw 400 for non-existant txid", async () => { - // Integration test - if (process.env.TEST !== "unit") { - req.params.txid = - "57b3082a2bf269b3d6f40fee7fb9c664e8256a88ca5ee2697c05b94578223333" - - const result = await txDetails(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "TXID not found") - } - }) - - it("should get tx details with token info", async () => { - if (process.env.TEST === "unit") { - // Mock the slpjs library for unit tests. - pathStub.BitboxNetwork = slpjsMock.BitboxNetwork - txDetails = slpRouteStub.testableComponents.txDetails - } - - req.params.txid = - "57b3082a2bf269b3d6f40fee7fb9c664e8256a88ca5ee2697c05b9457822d446" - - const result = await txDetails(req, res) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.hasAnyKeys(result, ["tokenIsValid", "tokenInfo"]) - }) - }) - - describe("txsTokenIdAddressSingle()", () => { - const txsTokenIdAddressSingle = - slpRoute.testableComponents.txsTokenIdAddressSingle - - it("should throw 400 if tokenId is empty", async () => { - req.params.tokenId = "" - const result = await txsTokenIdAddressSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "tokenId can not be empty") - }) - - it("should throw 400 if address is empty", async () => { - req.params.tokenId = - "495322b37d6b2eae81f045eda612b95870a0c2b6069c58f70cf8ef4e6a9fd43a" - req.params.address = "" - const result = await txsTokenIdAddressSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "address can not be empty") - }) - /* - it("should get tx details with tokenId and address", async () => { - if (process.env.TEST === "unit") { - nock(`${process.env.SLPDB_URL}`) - .get(uri => uri.includes("/")) - .reply(200, { - c: mockData.mockTransactions - }) - } - - //req.params.tokenId = - // "37279c7dc81ceb34d12f03344b601c582e931e05d0e552c29c428bfa39d39af3" - //req.params.address = "slptest:qr83cu3p7yg9yac7qthwm0nul2ev2kukvsqmes3vl0" - - req.params.tokenId = - "7ac7f4bb50b019fe0f5c81e3fc13fc0720e130282ea460768cafb49785eb2796" - req.params.address = "slptest:qpwa35xq0q0cnmdu0rwzkct369hddzsqpsqdzw6h9h" - - - const result = await txsTokenIdAddressSingle(req, res) - console.log(`result: ${JSON.stringify(result, null, 2)}`) - - assert.hasAnyKeys(result[0], ["txid", "tokenDetails"]) - }) -*/ - }) -}) diff --git a/test/v2/transaction.js b/test/v2/transaction.js deleted file mode 100644 index 4eb1bdc..0000000 --- a/test/v2/transaction.js +++ /dev/null @@ -1,333 +0,0 @@ -/* - TESTS FOR THE TRANSACTION.TS LIBRARY - - This test file uses the environment variable TEST to switch between unit - and integration tests. By default, TEST is set to 'unit'. Set this variable - to 'integration' to run the tests against BCH mainnet. - - TODO: - -See "should throw an error for an invalid txid" for detailsSingle: - --The error handler should be refactored to return an intelligent error message, - instead of the 503 error it is returning now. -*/ - -"use strict" - -const chai = require("chai") -const assert = chai.assert -const transactionRoute = require("../../src/routes/v2/transaction") -const nock = require("nock") // HTTP mocking - -let originalEnvVars // Used during transition from integration to unit tests. - -// Mocking data. -const { mockReq, mockRes } = require("./mocks/express-mocks") -const mockData = require("./mocks/transaction-mocks") - -// Used for debugging. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -describe("#Transactions", () => { - let req, res - - before(() => { - // Save existing environment variables. - originalEnvVars = { - BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL, - RPC_BASEURL: process.env.RPC_BASEURL, - RPC_USERNAME: process.env.RPC_USERNAME, - RPC_PASSWORD: process.env.RPC_PASSWORD - } - - // Set default environment variables for unit tests. - if (!process.env.TEST) process.env.TEST = "unit" - if (process.env.TEST === "unit") { - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - process.env.RPC_BASEURL = "http://fakeurl/api" - process.env.RPC_USERNAME = "fakeusername" - process.env.RPC_PASSWORD = "fakepassword" - } - }) - - // Setup the mocks before each test. - beforeEach(() => { - // Mock the req and res objects used by Express routes. - req = mockReq - res = mockRes - - // Explicitly reset the parmas and body. - req.params = {} - req.body = {} - req.query = {} - - // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() - }) - - afterEach(() => { - // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() - }) - - after(() => { - // Restore any pre-existing environment variables. - process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL - process.env.RPC_BASEURL = originalEnvVars.RPC_BASEURL - process.env.RPC_USERNAME = originalEnvVars.RPC_USERNAME - process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD - }) - - describe("#root", async () => { - // root route handler. - const root = transactionRoute.testableComponents.root - - it("should respond to GET for base route", async () => { - const result = root(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(result.status, "transaction", "Returns static string") - }) - }) - - describe("#detailsBulk", async () => { - const detailsBulk = transactionRoute.testableComponents.detailsBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await detailsBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "txids needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single txid", async () => { - req.body = { - txids: `6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40` - } - - const result = await detailsBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "txids needs to be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid txid", async () => { - const fakeTXID = `02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/tx/${fakeTXID}`) - .reply(400, { - result: { error: "parameter 1 must be hexadecimal string" } - }) - } - - req.body = { - txids: [fakeTXID] - } - - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - }) - - it("should process a single txid", async () => { - const txid = `6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/tx/${txid}`) - .reply(200, mockData.mockDetails) - } - - req.body = { - txids: [txid] - } - - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAnyKeys(result[0], [ - "txid", - "version", - "locktime", - "vin", - "vout", - "blockhash", - "blockheight", - "confirmations", - "time", - "blocktime", - "valueOut", - "size", - "valueIn", - "fees" - ]) - }) - - it("should process a multiple txids", async () => { - const txid1 = `6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40` - const txid2 = `8d4fd4dcaa9d8051dc7d862dc23d8aa23e20b77b9c928c49380685459caa7043` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/tx/${txid1}`) - .reply(200, mockData.mockDetails) - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/tx/${txid2}`) - .reply(200, mockData.mockDetails) - } - - req.body = { - txids: [txid1, txid2] - } - - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAnyKeys(result[0], [ - "txid", - "version", - "locktime", - "vin", - "vout", - "blockhash", - "blockheight", - "confirmations", - "time", - "blocktime", - "valueOut", - "size", - "valueIn", - "fees" - ]) - }) - }) - - describe("#detailsSingle", () => { - // details route handler. - const detailsSingle = transactionRoute.testableComponents.detailsSingle - - it("should throw 400 if txid is empty", async () => { - const result = await detailsSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "txid can not be empty") - }) - - it("should error on an array", async () => { - req.params.txid = [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - - const result = await detailsSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "txid can not be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid txid", async () => { - if (process.env.TEST !== "unit") { - req.params.txid = `02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - const result = await detailsSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // The error handling code should probably be updated to respond with a better - // error message. - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "parameter 1 must be hexadecimal string", - "Proper error message" - ) - } - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.params.txid = `6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40` - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await detailsSingle(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or greater expected." - ) - //assert.include(result.error,"Network error: Could not communicate with full node","Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get details for a single address", async () => { - const txid = `6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40` - req.params.txid = txid - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/tx/${txid}`) - .reply(200, mockData.mockDetails) - } - - // Call the details API. - const result = await detailsSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert that required fields exist in the returned object. - assert.hasAllKeys(result, [ - "txid", - "version", - "locktime", - "vin", - "vout", - "blockhash", - "blockheight", - "confirmations", - "time", - "blocktime", - "valueOut", - "size", - "valueIn", - "fees" - ]) - assert.isArray(result.vin) - assert.isArray(result.vout) - }) - }) -}) diff --git a/test/v2/util.js b/test/v2/util.js deleted file mode 100644 index e550f14..0000000 --- a/test/v2/util.js +++ /dev/null @@ -1,300 +0,0 @@ -/* - TESTS FOR THE UTIL.TS LIBRARY - - This test file uses the environment variable TEST to switch between unit - and integration tests. By default, TEST is set to 'unit'. Set this variable - to 'integration' to run the tests against BCH mainnet. -*/ - -"use strict" - -const chai = require("chai") -const assert = chai.assert -const utilRoute = require("../../src/routes/v2/util") -const nock = require("nock") // HTTP mocking - -let originalEnvVars // Used during transition from integration to unit tests. - -// Mocking data. -const { mockReq, mockRes } = require("./mocks/express-mocks") -const mockData = require("./mocks/util-mocks") - -// Used for debugging. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -describe("#Util", () => { - let req, res - - before(() => { - // Save existing environment variables. - originalEnvVars = { - BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL, - RPC_BASEURL: process.env.RPC_BASEURL, - RPC_USERNAME: process.env.RPC_USERNAME, - RPC_PASSWORD: process.env.RPC_PASSWORD - } - - // Set default environment variables for unit tests. - if (!process.env.TEST) process.env.TEST = "unit" - if (process.env.TEST === "unit") { - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - process.env.RPC_BASEURL = "http://fakeurl/api" - process.env.RPC_USERNAME = "fakeusername" - process.env.RPC_PASSWORD = "fakepassword" - } - }) - - // Setup the mocks before each test. - beforeEach(() => { - // Mock the req and res objects used by Express routes. - req = mockReq - res = mockRes - - // Explicitly reset the parmas and body. - req.params = {} - req.body = {} - req.query = {} - - // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() - }) - - afterEach(() => { - // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() - }) - - after(() => { - // Restore any pre-existing environment variables. - process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL - process.env.RPC_BASEURL = originalEnvVars.RPC_BASEURL - process.env.RPC_USERNAME = originalEnvVars.RPC_USERNAME - process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD - }) - - describe("#root", async () => { - // root route handler. - const root = utilRoute.testableComponents.root - - it("should respond to GET for base route", async () => { - const result = root(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(result.status, "util", "Returns static string") - }) - }) - - describe("#validateAddressSingle", async () => { - const validateAddress = utilRoute.testableComponents.validateAddressSingle - - it("should throw an error for an empty address", async () => { - const result = await validateAddress(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "address can not be empty", - "Proper error message" - ) - }) - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - req.params.address = `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y` - - const result = await validateAddress(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or greater expected." - ) - //assert.include(result.error,"Network error: Could not communicate with full node","Error message expected") - }) - - it("should validate address", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockAddress }) - } - - req.params.address = `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y` - - const result = await validateAddress(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, [ - "isvalid", - "address", - "scriptPubKey", - "ismine", - "iswatchonly", - "isscript" - ]) - }) - }) - - describe("#validateAddressBulk", async () => { - const validateAddressBulk = utilRoute.testableComponents.validateAddressBulk - - it("should throw an error for an empty body", async () => { - const result = await validateAddressBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array. Use GET for single address.", - "Proper error message" - ) - }) - - it("should error on non-array single address", async () => { - req.body = { - addresses: `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y` - } - - const result = await validateAddressBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array. Use GET for single address.", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.addresses = testArray - - const result = await validateAddressBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should error on invalid address", async () => { - req.body = { - addresses: [`bchtest:qqqk4y6lsl5da64sg5qc3xezmpl`] - } - - const result = await validateAddressBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address. Double check your address is valid", - "Proper error message" - ) - }) - - it("should error on mainnet address when using testnet", async () => { - req.body = { - addresses: [`bitcoincash:qrcc3jsqpgwqcdru70sk54sd0g3l04q7c53ycm6ucj`] - } - - const result = await validateAddressBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid network. Trying to use a testnet address on mainnet, or vice versa.", - "Proper error message" - ) - }) - - it("should throw 503 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.RPC_BASEURL = "http://fakeurl/api/" - - req.body.addresses = [ - `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y` - ] - - const result = await validateAddressBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.RPC_BASEURL = savedUrl2 - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or greater expected." - ) - }) - - it("should validate a single address", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .reply(200, { result: mockData.mockAddress }) - } - - req.body.addresses = [ - `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y` - ] - - const result = await validateAddressBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAllKeys(result[0], [ - "isvalid", - "address", - "scriptPubKey", - "ismine", - "iswatchonly", - "isscript" - ]) - }) - - it("should validate a multiple addresses", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(``) - .times(2) - .reply(200, { result: mockData.mockAddress }) - } - - req.body.addresses = [ - `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y`, - `bchtest:qqqk4y6lsl5da64sg5qc3xezmplyu5kmpyz2ysaa5y` - ] - - const result = await validateAddressBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAllKeys(result[0], [ - "isvalid", - "address", - "scriptPubKey", - "ismine", - "iswatchonly", - "isscript" - ]) - }) - }) -}) diff --git a/test/v3/blockchain.js b/test/v3/blockchain.js index 2101961..fe1d70b 100644 --- a/test/v3/blockchain.js +++ b/test/v3/blockchain.js @@ -11,7 +11,7 @@ const chai = require("chai") const assert = chai.assert const nock = require("nock") // HTTP mocking -const blockchainRoute = require("../../src/routes/v3/blockchain") +const blockchainRoute = require("../../src/routes/v3/full-node/blockchain") const util = require("util") util.inspect.defaultOptions = { depth: 1 } diff --git a/test/v3/control.js b/test/v3/control.js index dd2d799..e651b26 100644 --- a/test/v3/control.js +++ b/test/v3/control.js @@ -11,7 +11,7 @@ const chai = require("chai") const assert = chai.assert -const controlRoute = require("../../src/routes/v3/control") +const controlRoute = require("../../src/routes/v3/full-node/control") const nock = require("nock") // HTTP mocking let originalEnvVars // Used during transition from integration to unit tests. diff --git a/test/v3/mining.js b/test/v3/mining.js index e9a8b65..7ba81c4 100644 --- a/test/v3/mining.js +++ b/test/v3/mining.js @@ -10,7 +10,7 @@ const chai = require("chai") const assert = chai.assert -const miningRoute = require("../../src/routes/v3/mining") +const miningRoute = require("../../src/routes/v3/full-node/mining") const nock = require("nock") // HTTP mocking let originalEnvVars // Used during transition from integration to unit tests. diff --git a/test/v3/rate-limits.js b/test/v3/rate-limits.js index 3b2db1f..0d81b5b 100644 --- a/test/v3/rate-limits.js +++ b/test/v3/rate-limits.js @@ -13,7 +13,7 @@ const { mockReq, mockRes, mockNext } = require("./mocks/express-mocks") // Libraries under test let rateLimitMiddleware = require("../../src/middleware/route-ratelimit") -const controlRoute = require("../../src/routes/v3/control") +const controlRoute = require("../../src/routes/v3/full-node/control") let req, res, next let originalEnvVars // Used during transition from integration to unit tests. diff --git a/test/v3/raw-transactions.js b/test/v3/raw-transactions.js index c6af05e..bf1c78b 100644 --- a/test/v3/raw-transactions.js +++ b/test/v3/raw-transactions.js @@ -14,7 +14,7 @@ const chai = require("chai") const assert = chai.assert -const rawtransactions = require("../../src/routes/v3/rawtransactions") +const rawtransactions = require("../../src/routes/v3/full-node/rawtransactions") const nock = require("nock") // HTTP mocking let originalEnvVars // Used during transition from integration to unit tests. From 1886a2cb3f9a6e5e806afbba7b433080db3c70af Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Thu, 31 Oct 2019 16:47:52 -0700 Subject: [PATCH 08/49] feat(tiers): Enforcing tiered access to endpoints --- src/app.js | 3 +- src/middleware/jwt-auth.js | 60 ++++++++++++++++++--- test/v3/rate-limits.js | 104 +++++++++++++++++++++++++++++++++++-- 3 files changed, 156 insertions(+), 11 deletions(-) diff --git a/src/app.js b/src/app.js index 99a8a29..7f04668 100644 --- a/src/app.js +++ b/src/app.js @@ -75,7 +75,8 @@ const auth = new AuthMW() app.use(`/${v3prefix}/`, auth.mw()) // Rate limit on all v3 routes -app.use(`/${v3prefix}/`, routeRateLimit) +app.use(`/${v3prefix}/`, routeRateLimit) // Establish and enforce rate limits. +app.use(`/${v3prefix}/`, jwtAuth.routeAccess) // Enforce access tiers. app.use(`/${v3prefix}/` + `health-check`, healthCheckV3) app.use(`/${v3prefix}/` + `blockchain`, blockchainV3.router) app.use(`/${v3prefix}/` + `control`, controlV3.router) diff --git a/src/middleware/jwt-auth.js b/src/middleware/jwt-auth.js index b006035..684f95a 100644 --- a/src/middleware/jwt-auth.js +++ b/src/middleware/jwt-auth.js @@ -10,12 +10,9 @@ // req.locals.jwtToken property. const getTokenFromHeaders = (req, res, next) => { try { - // console.log(`getTokenFromHeaders2: Searching headers for a JWT token.`) - - // console.log(`req.headers: `, req.headers) - - // If the authorization header exists. + // Only executes if the authorization header exists. if (req.headers.authorization) { + // Retrieve the auth string from the header object. const authStr = req.headers.authorization // If the header is proceeded by the word 'Token' @@ -31,18 +28,67 @@ const getTokenFromHeaders = (req, res, next) => { proLimit: false, apiLevel: 0 } + } else { + req.locals.jwtToken = token } } } } catch (err) { - console.log(`Error in getTokenFromHeaders2: `, err) + console.log(`Error in getTokenFromHeaders: `, err) + } + + next() +} + +// This middleware analyizes the combination of JWT token and apiLevel. It will +// stop users from accessing routes they have not paid for. +// +// It is assumed this middleware is run AFTER the route-ratelimit.js middleware, +// so that the req.locals.apiLevel property has been populated. +const routeAccess = (req, res, next) => { + try { + // console.log(`req.locals: ${JSON.stringify(req.locals, null, 2)}`) + console.log(`req.url: `, req.url) + + const locals = req.locals + const url = req.url + + const level20Routes = ["insight", "bitcore", "blockbook"] + + // JWT token is included in header. + if (!!locals.jwtToken && locals.jwtToken !== "") { + // API Level is 0 (free tier): do nothing. All endpoints are open to + // free public access within the drastically limited rate limits. + + if (locals.apiLevel === 10) { + // API level is 10 (full node tier) + + // Loop through the routes that are not accessible to this tier. + for (let i = 0; i < level20Routes.length; i++) { + // If the requested route is for a higher tier, return a 403. + if (url.indexOf(level20Routes[i]) > -1) { + res.status(403) + return res.json({ + error: + "route is not accessible for your JWT tier. Upgrade or use anonymous access." + }) + } + } + } + // else if (locals.apiLevel > 11) { + // // API level is 20 (indexer tier) + // } + } + } catch (err) { + console.log(`Error in routeAccess: `, err) } next() } const jwtAuth = { - getTokenFromHeaders + getTokenFromHeaders, + routeAccess } module.exports = jwtAuth diff --git a/test/v3/rate-limits.js b/test/v3/rate-limits.js index 0d81b5b..6f74c42 100644 --- a/test/v3/rate-limits.js +++ b/test/v3/rate-limits.js @@ -14,11 +14,15 @@ const { mockReq, mockRes, mockNext } = require("./mocks/express-mocks") // Libraries under test let rateLimitMiddleware = require("../../src/middleware/route-ratelimit") const controlRoute = require("../../src/routes/v3/full-node/control") +const jwtAuth = require("../../src/middleware/jwt-auth") let req, res, next let originalEnvVars // Used during transition from integration to unit tests. -describe("#route-ratelimits", () => { +// JWT token used in tests. +const jwt = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkYWRlM2Y1NzM5ZTZjMGZmMDM0YjlhMSIsImlhdCI6MTU3MTY3NzQ1MCwiZXhwIjoxNTc0MjY5NDUwfQ.SSz7F7ETyBB3eoNG2VKCzPOhddtB-vrtmEoj7PxicrQ` + +describe("#route-ratelimits & jwt-auth", () => { before(() => { // Save existing environment variables. originalEnvVars = { @@ -35,8 +39,8 @@ describe("#route-ratelimits", () => { // Setup the mocks before each test. beforeEach(() => { // Mock the req and res objects used by Express routes. - req = mockReq - res = mockRes + req = Object.assign({}, mockReq) + res = Object.assign({}, mockRes) next = mockNext // Explicitly reset the parmas and body. @@ -45,6 +49,100 @@ describe("#route-ratelimits", () => { req.query = {} }) + describe("#jwt-auth.js", () => { + describe("#getTokenFromHeaders", () => { + it(`should populate the req.locals object correctly`, () => { + // Initialize req.locals + req.locals = { + proLimit: false, + apiLevel: 0 + } + + const header = `Token ${jwt}` + req.headers.authorization = header + + jwtAuth.getTokenFromHeaders(req, res, next) + + // console.log(`req.locals: ${JSON.stringify(req.locals, null, 2)}`) + + assert.property(req.locals, "proLimit") + assert.property(req.locals, "apiLevel") + assert.property(req.locals, "jwtToken") + assert.equal(req.locals.jwtToken, jwt) + }) + }) + + describe("#routeAccess", () => { + it("should do nothing if req.locals.jwtToken is undefined", () => { + // Initialize req.locals + req.locals = { + proLimit: false, + apiLevel: 0 + } + req.url = "/insight/address/details" + + // Reset the history of the stub and assert it has not been called. + res.status.resetHistory() + assert.equal(res.status.called, false, "stub history reset") + + jwtAuth.routeAccess(req, res, next) + + // console.log(`req.locals: ${JSON.stringify(req.locals, null, 2)}`) + assert.equal( + res.status.called, + false, + "stub should NOT have been called." + ) + }) + + it("should throw error if full-node tier tries to access indexer", () => { + // Initialize req.locals + req.locals = { + proLimit: true, + apiLevel: 10, + jwtToken: jwt + } + req.url = "/insight/address/details" + + try { + res.status.resetHistory() + assert.equal(res.status.called, false, "stub history reset") + + jwtAuth.routeAccess(req, res, next) + + assert.equal(res.status.called, true, "stub should have been called.") + } catch (err) { + console.log(`caught error: `, err) + } + }) + + it("should allow indexer tier tries access indexer endpoints", () => { + // Initialize req.locals + req.locals = { + proLimit: true, + apiLevel: 20, + jwtToken: jwt + } + req.url = "/insight/address/details" + + try { + res.status.resetHistory() + assert.equal(res.status.called, false, "stub history reset") + + jwtAuth.routeAccess(req, res, next) + + assert.equal( + res.status.called, + false, + "stub should NOT have been called." + ) + } catch (err) { + console.log(`caught error: `, err) + } + }) + }) + }) + describe("#routeRateLimit", () => { let routeRateLimit = rateLimitMiddleware.routeRateLimit const getInfo = controlRoute.testableComponents.getInfo From 39faf7cd4c5fea4ae9adc5c2c745ba0b24eaaaec Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 2 Nov 2019 13:49:10 -0700 Subject: [PATCH 09/49] Achieving desired rate limiting results. --- src/app.js | 2 +- src/middleware/jwt-auth.js | 4 +- src/middleware/route-ratelimit.js | 65 ++++++++++++++++++++++++++++--- 3 files changed, 62 insertions(+), 9 deletions(-) diff --git a/src/app.js b/src/app.js index 7f04668..20b90bc 100644 --- a/src/app.js +++ b/src/app.js @@ -76,7 +76,7 @@ app.use(`/${v3prefix}/`, auth.mw()) // Rate limit on all v3 routes app.use(`/${v3prefix}/`, routeRateLimit) // Establish and enforce rate limits. -app.use(`/${v3prefix}/`, jwtAuth.routeAccess) // Enforce access tiers. +// app.use(`/${v3prefix}/`, jwtAuth.routeAccess) // Enforce access tiers. app.use(`/${v3prefix}/` + `health-check`, healthCheckV3) app.use(`/${v3prefix}/` + `blockchain`, blockchainV3.router) app.use(`/${v3prefix}/` + `control`, controlV3.router) diff --git a/src/middleware/jwt-auth.js b/src/middleware/jwt-auth.js index 684f95a..be719fc 100644 --- a/src/middleware/jwt-auth.js +++ b/src/middleware/jwt-auth.js @@ -60,8 +60,8 @@ const routeAccess = (req, res, next) => { // API Level is 0 (free tier): do nothing. All endpoints are open to // free public access within the drastically limited rate limits. - if (locals.apiLevel === 10) { - // API level is 10 (full node tier) + if (locals.apiLevel < 20) { + // API level does not include indexer access. // Loop through the routes that are not accessible to this tier. for (let i = 0; i < level20Routes.length; i++) { diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index 2bb2c2b..fca640d 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -53,17 +53,35 @@ const routeRateLimit = async function(req, res, next) { if (req.locals.jwtToken) { // console.log(`req.locals.jwtToken: ${req.locals.jwtToken}`) + // URL for the auth server. const path = `${authServer}apitoken/isvalid/${req.locals.jwtToken}` + // Ask Auth server if the JWT token is valid. + // Get the API level for this user. let jwtInfo = await axios.get(path) jwtInfo = jwtInfo.data - // console.log(`jwtInfo: ${JSON.stringify(jwtInfo, null, 2)}`) + console.log(`jwtInfo: ${JSON.stringify(jwtInfo, null, 2)}`) - // Enable pro-tier rate limits if JWT if valid. + // If JWT if valid, evaluate the API level for the user. if (jwtInfo.isValid) { - // console.log(`JWT is valid. Enabling pro-tier rate limits.`) - req.locals.proLimit = true - req.locals.apiLevel = jwtInfo.apiLevel + // Set fine-grain permissions for each user based on the JWT token. + const userPermissions = evalUserPermissioins(req, jwtInfo) + console.log( + `userPermissions: ${JSON.stringify(userPermissions, null, 2)}` + ) + console.log(` `) + + req.locals.proLimit = userPermissions.proLimit + req.locals.apiLevel = userPermissions.apiLevel + + // const locals = req.locals + // console.log(`locals: ${JSON.stringify(locals, null, 2)}`) + // const url = req.url + // console.log(`url: ${JSON.stringify(url, null, 2)}`) + // + // // console.log(`JWT is valid. Enabling pro-tier rate limits.`) + // req.locals.proLimit = true + // req.locals.apiLevel = jwtInfo.apiLevel } } } @@ -126,7 +144,7 @@ const routeRateLimit = async function(req, res, next) { res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 return res.json({ - error: `Too many requests. Limits are ${maxRequests} requests per minute.` + error: `Too many requests. Your limits are currently ${maxRequests} requests per minute.` }) } }) @@ -139,4 +157,39 @@ const routeRateLimit = async function(req, res, next) { uniqueRateLimits[route](req, res, next) } +// This function returns the an object with proLimit and apiLevel properties. +// It does fine-grane analysis on the data coming from the auth servers and +// uses its output to adjust rate limits on-the-fly based on the users +// permission level. +function evalUserPermissioins(req, authData) { + console.log(`authData: ${JSON.stringify(authData, null, 2)}`) + + // Return object with default values + const retObj = { + proLimit: authData.isValid, + apiLevel: authData.apiLevel + } + + const level20Routes = ["insight", "bitcore", "blockbook"] + + const locals = req.locals + console.log(`locals: ${JSON.stringify(locals, null, 2)}`) + const url = req.url + console.log(`url: ${JSON.stringify(url, null, 2)}`) + + if (authData.apiLevel < 20) { + // Loop through the routes that are not accessible to this tier. + for (let i = 0; i < level20Routes.length; i++) { + // If the requested route is for a higher tier, + // revert to anonymous level permissions. + if (url.indexOf(level20Routes[i]) > -1) { + retObj.proLimit = false + retObj.apiLevel = 0 + } + } + } + + return retObj +} + module.exports = { routeRateLimit } From 1c1bb25a5458edd773cc01c61d272965a7195def Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 2 Nov 2019 16:24:38 -0700 Subject: [PATCH 10/49] Completed e2e bch-js rate limit tests --- src/app.js | 2 -- src/middleware/jwt-auth.js | 49 +----------------------------- src/middleware/route-ratelimit.js | 15 +++++---- src/routes/v3/full-node/control.js | 2 -- 4 files changed, 8 insertions(+), 60 deletions(-) diff --git a/src/app.js b/src/app.js index 20b90bc..26f8020 100644 --- a/src/app.js +++ b/src/app.js @@ -71,12 +71,10 @@ app.use(`/${v3prefix}/`, jwtAuth.getTokenFromHeaders) // Instantiate the authorization middleware, used to implement pro-tier rate limiting. const auth = new AuthMW() -// app.use(`/${v2prefix}/`, auth.mw()) app.use(`/${v3prefix}/`, auth.mw()) // Rate limit on all v3 routes app.use(`/${v3prefix}/`, routeRateLimit) // Establish and enforce rate limits. -// app.use(`/${v3prefix}/`, jwtAuth.routeAccess) // Enforce access tiers. app.use(`/${v3prefix}/` + `health-check`, healthCheckV3) app.use(`/${v3prefix}/` + `blockchain`, blockchainV3.router) app.use(`/${v3prefix}/` + `control`, controlV3.router) diff --git a/src/middleware/jwt-auth.js b/src/middleware/jwt-auth.js index be719fc..6880ea0 100644 --- a/src/middleware/jwt-auth.js +++ b/src/middleware/jwt-auth.js @@ -40,55 +40,8 @@ const getTokenFromHeaders = (req, res, next) => { next() } -// This middleware analyizes the combination of JWT token and apiLevel. It will -// stop users from accessing routes they have not paid for. -// -// It is assumed this middleware is run AFTER the route-ratelimit.js middleware, -// so that the req.locals.apiLevel property has been populated. -const routeAccess = (req, res, next) => { - try { - // console.log(`req.locals: ${JSON.stringify(req.locals, null, 2)}`) - console.log(`req.url: `, req.url) - - const locals = req.locals - const url = req.url - - const level20Routes = ["insight", "bitcore", "blockbook"] - - // JWT token is included in header. - if (!!locals.jwtToken && locals.jwtToken !== "") { - // API Level is 0 (free tier): do nothing. All endpoints are open to - // free public access within the drastically limited rate limits. - - if (locals.apiLevel < 20) { - // API level does not include indexer access. - - // Loop through the routes that are not accessible to this tier. - for (let i = 0; i < level20Routes.length; i++) { - // If the requested route is for a higher tier, return a 403. - if (url.indexOf(level20Routes[i]) > -1) { - res.status(403) - return res.json({ - error: - "route is not accessible for your JWT tier. Upgrade or use anonymous access." - }) - } - } - } - // else if (locals.apiLevel > 11) { - // // API level is 20 (indexer tier) - // } - } - } catch (err) { - console.log(`Error in routeAccess: `, err) - } - - next() -} - const jwtAuth = { - getTokenFromHeaders, - routeAccess + getTokenFromHeaders } module.exports = jwtAuth diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index fca640d..b67c045 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -60,16 +60,15 @@ const routeRateLimit = async function(req, res, next) { // Get the API level for this user. let jwtInfo = await axios.get(path) jwtInfo = jwtInfo.data - console.log(`jwtInfo: ${JSON.stringify(jwtInfo, null, 2)}`) + // console.log(`jwtInfo: ${JSON.stringify(jwtInfo, null, 2)}`) // If JWT if valid, evaluate the API level for the user. if (jwtInfo.isValid) { // Set fine-grain permissions for each user based on the JWT token. const userPermissions = evalUserPermissioins(req, jwtInfo) - console.log( - `userPermissions: ${JSON.stringify(userPermissions, null, 2)}` - ) - console.log(` `) + // console.log( + // `userPermissions: ${JSON.stringify(userPermissions, null, 2)}` + // ) req.locals.proLimit = userPermissions.proLimit req.locals.apiLevel = userPermissions.apiLevel @@ -162,7 +161,7 @@ const routeRateLimit = async function(req, res, next) { // uses its output to adjust rate limits on-the-fly based on the users // permission level. function evalUserPermissioins(req, authData) { - console.log(`authData: ${JSON.stringify(authData, null, 2)}`) + // console.log(`authData: ${JSON.stringify(authData, null, 2)}`) // Return object with default values const retObj = { @@ -173,9 +172,9 @@ function evalUserPermissioins(req, authData) { const level20Routes = ["insight", "bitcore", "blockbook"] const locals = req.locals - console.log(`locals: ${JSON.stringify(locals, null, 2)}`) + // console.log(`locals: ${JSON.stringify(locals, null, 2)}`) const url = req.url - console.log(`url: ${JSON.stringify(url, null, 2)}`) + // console.log(`url: ${JSON.stringify(url, null, 2)}`) if (authData.apiLevel < 20) { // Loop through the routes that are not accessible to this tier. diff --git a/src/routes/v3/full-node/control.js b/src/routes/v3/full-node/control.js index e2e6374..1a0ae1e 100644 --- a/src/routes/v3/full-node/control.js +++ b/src/routes/v3/full-node/control.js @@ -36,8 +36,6 @@ async function getNetworkInfo(req, res, next) { requestConfig } = routeUtils.setEnvVars() - console.log(`req.locals: ${JSON.stringify(req.locals, null, 2)}`) - requestConfig.data.id = "getnetworkinfo" requestConfig.data.method = "getnetworkinfo" requestConfig.data.params = [] From 4079bbdd4850b8da129f4396a7442dbd6d0c4fee Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 3 Nov 2019 17:33:34 -0800 Subject: [PATCH 11/49] fix(array limits): 20 elem array enforcement for all tiers --- src/routes/v3/route-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/v3/route-utils.js b/src/routes/v3/route-utils.js index be11a4e..d49b29d 100644 --- a/src/routes/v3/route-utils.js +++ b/src/routes/v3/route-utils.js @@ -26,7 +26,7 @@ module.exports = { // or not. function validateArraySize(req, array) { const FREEMIUM_INPUT_SIZE = 20 - const PRO_INPUT_SIZE = 100 + const PRO_INPUT_SIZE = 20 if (req.locals && req.locals.proLimit) { if (array.length <= PRO_INPUT_SIZE) return true From b158321e7978d35ffc8cad2fbf6611f69041e9c7 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2019 19:05:06 +0000 Subject: [PATCH 12/49] fix(package): update @chris.troutner/bch-js to version 2.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 36617f3..88ec0eb 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "node": ">=10.15.1" }, "dependencies": { - "@chris.troutner/bch-js": "^1.6.1", + "@chris.troutner/bch-js": "^2.0.0", "apidoc": "^0.17.7", "axios": "^0.19.0", "body-parser": "^1.18.3", From 5d571ea0bc50435d92a5db62ed7c904f4534f510 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Mon, 4 Nov 2019 19:05:11 +0000 Subject: [PATCH 13/49] chore(package): update lockfile package-lock.json --- package-lock.json | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6070ee0..d24396a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -101,9 +101,9 @@ } }, "@chris.troutner/bch-js": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@chris.troutner/bch-js/-/bch-js-1.6.1.tgz", - "integrity": "sha512-Wkv7iVw9wYjNF5/Ewy5Qq995x1AV+DWRqikZg9FQxCl7Tub7PmmDfADcJwp+tNKGZs5Q+dpiL30P6zBld52g7g==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@chris.troutner/bch-js/-/bch-js-2.0.0.tgz", + "integrity": "sha512-ufb2pthdZtXAOVkB/G1jhTrdRML9moIaN8F28rBTq8I7nfh59A4MSRtgr5lFvahs/sHsWmhYu5OAVoSZKDADWA==", "requires": { "apidoc": "^0.17.7", "assert": "^2.0.0", @@ -1543,12 +1543,22 @@ } }, "bchaddrjs-slp": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/bchaddrjs-slp/-/bchaddrjs-slp-0.2.5.tgz", - "integrity": "sha512-33flmPcqMFswerKu7477DSUNMVMQR3tHDk3lvbmsdkEva+TxVGGWWE/p5Lqx9M/8t3vkbe7fzmVhj4QhChcCyA==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/bchaddrjs-slp/-/bchaddrjs-slp-0.2.7.tgz", + "integrity": "sha512-wqd4RFMGWzT3ukNbIzLR3pG6EMpcbCUcqXHa0UsegNKkKXZUFXg3dIfa01MRFr4R9D4jnJgDCgY0pa4B4ih4vQ==", "requires": { "bs58check": "^2.1.2", - "cashaddrjs-slp": "^0.2.11" + "cashaddrjs-slp": "^0.2.12" + }, + "dependencies": { + "cashaddrjs-slp": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/cashaddrjs-slp/-/cashaddrjs-slp-0.2.12.tgz", + "integrity": "sha512-n2TTIuW6vZZxYvjvsUAA+wOM0Zkj+3RRKUtDC1XSu4Ic4XVr0yFJkl1bzQkHWda7nkVT51sxjZneygz7D0SyrQ==", + "requires": { + "big-integer": "^1.6.34" + } + } } }, "bcrypt-pbkdf": { From eb6b2e6ff34837a700c10b50f9ae21af2d60e3e3 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Wed, 13 Nov 2019 08:09:58 -0800 Subject: [PATCH 14/49] Commented out failing unit tests --- test/v3/rate-limits.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/v3/rate-limits.js b/test/v3/rate-limits.js index 6f74c42..50d7ca6 100644 --- a/test/v3/rate-limits.js +++ b/test/v3/rate-limits.js @@ -72,6 +72,9 @@ describe("#route-ratelimits & jwt-auth", () => { }) }) + // TODO: This code has been refactored and these unit tests no longer apply. + // I think I forgot to create new unit tests to reflect the change in code though. + /* describe("#routeAccess", () => { it("should do nothing if req.locals.jwtToken is undefined", () => { // Initialize req.locals @@ -141,6 +144,7 @@ describe("#route-ratelimits & jwt-auth", () => { } }) }) + */ }) describe("#routeRateLimit", () => { From ccac98dd5e031bcec1dfae4caf73b7c393c81004 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Wed, 13 Nov 2019 08:40:37 -0800 Subject: [PATCH 15/49] Fixed integration test for gettxout --- src/routes/v3/full-node/blockchain.js | 2 ++ test/v3/blockchain.js | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/routes/v3/full-node/blockchain.js b/src/routes/v3/full-node/blockchain.js index 8e2403b..cd3d556 100644 --- a/src/routes/v3/full-node/blockchain.js +++ b/src/routes/v3/full-node/blockchain.js @@ -732,6 +732,8 @@ async function getTxOut(req, res, next) { requestConfig.data.method = "gettxout" requestConfig.data.params = [txid, n, include_mempool] + // console.log(`requestConfig: ${JSON.stringify(requestConfig, null, 2)}`) + const response = await BitboxHTTP(requestConfig) return res.json(response.data.result) diff --git a/test/v3/blockchain.js b/test/v3/blockchain.js index fe1d70b..aee8ed5 100644 --- a/test/v3/blockchain.js +++ b/test/v3/blockchain.js @@ -840,11 +840,12 @@ describe("#BlockchainRouter", () => { .reply(200, { result: mockData.mockTxOut }) } - req.params.txid = `2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266` + req.params.txid = `197dcda59864b1eee05498fd3c52cad787ec56ab7e635503cb39f9ab6f295d5d` req.params.n = 1 + req.query.include_mempool = "true" const result = await getTxOut(req, res) - //console.log(`result: ${JSON.stringify(result, null, 2)}`) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) assert.hasAllKeys(result, [ "bestblock", From 1f6f96721dd1897ab2214ecfa113711e6348246d Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Wed, 13 Nov 2019 08:47:08 -0800 Subject: [PATCH 16/49] Fixed communication with Insight API --- src/routes/v3/insight/address.js | 14 +++++++++----- test/v3/insight-address.js | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/routes/v3/insight/address.js b/src/routes/v3/insight/address.js index 165156a..9603682 100644 --- a/src/routes/v3/insight/address.js +++ b/src/routes/v3/insight/address.js @@ -45,8 +45,8 @@ function root(req, res, next) { // Returns a Promise. async function detailsFromInsight(thisAddress, currentPage = 0) { try { - //const addr = bchjs.Address.toLegacyAddress(thisAddress) - const addr = bchjs.Address.toCashAddress(thisAddress) + const addr = bchjs.Address.toLegacyAddress(thisAddress) + // const addr = bchjs.Address.toCashAddress(thisAddress) let path = `${process.env.BITCOINCOM_BASEURL}addr/${addr}` @@ -56,12 +56,12 @@ async function detailsFromInsight(thisAddress, currentPage = 0) { const to = from + PAGE_SIZE path = `${path}?from=${from}&to=${to}` - //console.log(`insight path: ${path}`) + // console.log(`insight path: ${path}`) // Query the Insight server. const axiosResponse = await axios.get(path) const retData = axiosResponse.data - //console.log(`retData: ${util.inspect(retData)}`) + // console.log(`retData: ${util.inspect(retData)}`) // Calculate pagesTotal from response const pagesTotal = Math.ceil(retData.txApperances / PAGE_SIZE) @@ -254,7 +254,8 @@ async function detailsSingle(req, res, next) { // Retrieve UTXO data from the Insight API async function utxoFromInsight(thisAddress) { try { - const addr = bchjs.Address.toCashAddress(thisAddress) + const addr = bchjs.Address.toLegacyAddress(thisAddress) + // const addr = bchjs.Address.toCashAddress(thisAddress) const path = `${process.env.BITCOINCOM_BASEURL}addr/${addr}/utxo` @@ -288,6 +289,7 @@ async function utxoFromInsight(thisAddress) { throw err } } + /** * @api {post} /address/utxo Get Address utxos bulk. * @apiName Address utxos bulk. @@ -449,6 +451,7 @@ async function utxoSingle(req, res, next) { return res.json({ error: util.inspect(err) }) } } + /** * @api {post} /address/utxo Get Unconfirmed transactions bulk. * @apiName Unconfirmed transactions bulk @@ -545,6 +548,7 @@ async function unconfirmedBulk(req, res, next) { return res.json({ error: util.inspect(err) }) } } + /** * @api {get} /insight/address/utxo/{address} Get Unconfirmed transactions single. * @apiName Unconfirmed transactions single. diff --git a/test/v3/insight-address.js b/test/v3/insight-address.js index e5a280b..992141b 100644 --- a/test/v3/insight-address.js +++ b/test/v3/insight-address.js @@ -197,7 +197,7 @@ describe("#Insight Address", () => { // Call the details API. const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) + // console.log(`result: ${util.inspect(result)}`) // Assert current page defaults to 0 assert.equal(result[0].currentPage, 0) From 8eac534d26095eb7f41aa821237f8e09d9824193 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2019 16:57:48 +0000 Subject: [PATCH 17/49] fix(package): update apidoc to version 0.18.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 36617f3..41fdde3 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ }, "dependencies": { "@chris.troutner/bch-js": "^1.6.1", - "apidoc": "^0.17.7", + "apidoc": "^0.18.0", "axios": "^0.19.0", "body-parser": "^1.18.3", "cookie-parser": "~1.4.3", From 252e6b02e6ce121a3c1c31e97332e832de689d51 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Wed, 13 Nov 2019 16:57:53 +0000 Subject: [PATCH 18/49] chore(package): update lockfile package-lock.json --- package-lock.json | 53 ++++++++++++++--------------------------------- 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6070ee0..f670a41 100644 --- a/package-lock.json +++ b/package-lock.json @@ -105,7 +105,6 @@ "resolved": "https://registry.npmjs.org/@chris.troutner/bch-js/-/bch-js-1.6.1.tgz", "integrity": "sha512-Wkv7iVw9wYjNF5/Ewy5Qq995x1AV+DWRqikZg9FQxCl7Tub7PmmDfADcJwp+tNKGZs5Q+dpiL30P6zBld52g7g==", "requires": { - "apidoc": "^0.17.7", "assert": "^2.0.0", "axios": "^0.19.0", "bc-bip68": "^1.0.5", @@ -905,40 +904,22 @@ } }, "apidoc": { - "version": "0.17.7", - "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.17.7.tgz", - "integrity": "sha512-9Wf4bRPwCuWOIOxR42dDnsXnFw+rhJg5VrMQK+KmNxJwyIh30UqX6gvjjXSG6YO74MqE87F18bbQXUENK9dPGg==", + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.18.0.tgz", + "integrity": "sha512-6gDuxCizXYzFap5JVEdXsATvVsx0Q2waZO39AHhFxFdikvfQ37U8GKASgJugTJNltWjnyUj2YasquUZw19Wmeg==", "requires": { - "apidoc-core": "~0.8.2", - "commander": "^2.19.0", - "fs-extra": "^7.0.0", - "lodash": "^4.17.10", - "markdown-it": "^8.3.1", - "winston": "^3.0.0" + "apidoc-core": "~0.8.3", + "commander": "^2.20.0", + "fs-extra": "^8.1.0", + "lodash": "^4.17.15", + "markdown-it": "^10.0.0", + "winston": "^3.2.1" }, "dependencies": { "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "requires": { - "graceful-fs": "^4.1.6" - } } } }, @@ -3574,9 +3555,9 @@ } }, "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", + "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==" }, "env-ci": { "version": "4.5.1", @@ -5231,7 +5212,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, "requires": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", @@ -5242,7 +5222,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, "requires": { "graceful-fs": "^4.1.6" } @@ -7503,12 +7482,12 @@ } }, "markdown-it": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", - "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", + "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", "requires": { "argparse": "^1.0.7", - "entities": "~1.1.1", + "entities": "~2.0.0", "linkify-it": "^2.0.0", "mdurl": "^1.0.1", "uc.micro": "^1.0.5" From 764c601373ebc61a6889d6687e21fae6ed11e67b Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Wed, 13 Nov 2019 11:59:56 -0800 Subject: [PATCH 19/49] Updated integration tests --- test/v3/raw-transactions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/v3/raw-transactions.js b/test/v3/raw-transactions.js index bf1c78b..014f9b5 100644 --- a/test/v3/raw-transactions.js +++ b/test/v3/raw-transactions.js @@ -692,7 +692,7 @@ describe("#Raw-Transactions", () => { // Integration test } else { assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "transaction already in block chain") + assert.include(result.error, "Missing inputs") } }) }) @@ -800,7 +800,7 @@ describe("#Raw-Transactions", () => { // Integration test } else { assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "transaction already in block chain") + assert.include(result.error, "Missing inputs") } }) }) From 8be3428e6f9e6228c72dacc4aa997574f16fa6d1 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 18 Nov 2019 20:08:49 -0800 Subject: [PATCH 20/49] Fixing broken integration test --- test/v3/blockchain.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/v3/blockchain.js b/test/v3/blockchain.js index aee8ed5..7a75b08 100644 --- a/test/v3/blockchain.js +++ b/test/v3/blockchain.js @@ -841,7 +841,7 @@ describe("#BlockchainRouter", () => { } req.params.txid = `197dcda59864b1eee05498fd3c52cad787ec56ab7e635503cb39f9ab6f295d5d` - req.params.n = 1 + req.params.n = 0 req.query.include_mempool = "true" const result = await getTxOut(req, res) From 719490d852874cd895d00f2de6719db8fe0db94d Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 18 Nov 2019 21:04:12 -0800 Subject: [PATCH 21/49] fix(request logging): Logging requests to winston --- src/app.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/app.js b/src/app.js index 26f8020..9aa93d1 100644 --- a/src/app.js +++ b/src/app.js @@ -57,7 +57,19 @@ app.set("view engine", "jade") // Mount the docs app.use("/docs", express.static(`${__dirname}/../docs`)) -app.use(logger("dev")) +// Log each request to the console with IP addresses. +// app.use(logger("dev")) +const morganFormat = `:remote-addr :remote-user :method :url :status :response-time ms - :res[content-length] :user-agent` +app.use(logger(morganFormat)) + +// Log the same data to the winston logs. +const logStream = { + write: function(message, encoding) { + wlogger.info(`request: ${message}`) + } +} +app.use(logger(morganFormat, { stream: logStream })) + app.use(bodyParser.json()) app.use(bodyParser.urlencoded({ extended: false })) app.use(cookieParser()) From 6d4dafdc14680a85aa31ec7f008cd547af5eb8af Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 25 Nov 2019 20:44:37 -0800 Subject: [PATCH 22/49] Removing slp-sdk and slpjs dependencies --- package-lock.json | 611 ++---------------------------------------- package.json | 2 - src/routes/v3/slp.js | 12 - test/v3/blockchain.js | 2 +- 4 files changed, 16 insertions(+), 611 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8149bb8..638bc0f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -335,9 +335,9 @@ } }, "@semantic-release/npm": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-5.3.2.tgz", - "integrity": "sha512-4uE4pYvf5XEWqJPTLCrrmp5zu9XnnLqDjsn+2yrpMICNCpTq3M5g9OD7hwQoolg5v373t2ZnNgdcjsz+lqU0aA==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-5.3.4.tgz", + "integrity": "sha512-XjITNRA/oOpJ7BfHk/WaOHs1WniYBszTde/bwADjjk1Luacpxg87jbDQVVt/oA3Zlx+MelxACRIEuRiPC5gu8g==", "dev": true, "requires": { "@semantic-release/error": "^2.2.0", @@ -366,9 +366,9 @@ } }, "execa": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-3.2.0.tgz", - "integrity": "sha512-kJJfVbI/lZE1PZYDI5VPxp8zXPO9rtxOkhpZ0jMKha56AI9y2gGVC6bkukStQf0ka5Rh15BA5m7cCCH4jmHqkw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz", + "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==", "dev": true, "requires": { "cross-spawn": "^7.0.0", @@ -441,9 +441,9 @@ } }, "path-key": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.0.tgz", - "integrity": "sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "read-pkg": { @@ -484,9 +484,9 @@ "dev": true }, "which": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.1.tgz", - "integrity": "sha512-N7GBZOTswtB9lkQBZA4+zAXrjEIWAUOB93AvzUiudRzRxhUdLURQ7D/gAIMY1gatT/LTbmbcv8SiYazy3eYB7w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -642,19 +642,6 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, - "@types/bigi": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@types/bigi/-/bigi-1.4.2.tgz", - "integrity": "sha512-St8Vm0x1ApYlU9yNaFx3jBis5JVU6oR/5Xtgvn8+N8Ts8f3ze6kOvAAg0aNkbGMGhhG6PrP0nMOgDI9NMFETkA==" - }, - "@types/bip39": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@types/bip39/-/bip39-2.4.2.tgz", - "integrity": "sha512-Vo9lqOIRq8uoIzEVrV87ZvcIM0PN9t0K3oYZ/CS61fIYKCBdOIM7mlWzXuRvSXrDtVa1uUO2w1cdfufxTC0bzg==", - "requires": { - "@types/node": "*" - } - }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -672,11 +659,6 @@ "@types/node": "*" } }, - "@types/lodash": { - "version": "4.14.144", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.144.tgz", - "integrity": "sha512-ogI4g9W5qIQQUhXAclq6zhqgqNUr7UlFaqDHbch7WLSLeeM/7d3CRaw7GLajxvyFvhJqw4Rpcz5bhoaYtIx6Tg==" - }, "@types/minimatch": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", @@ -694,28 +676,12 @@ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, - "@types/randombytes": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/randombytes/-/randombytes-2.0.0.tgz", - "integrity": "sha512-bz8PhAVlwN72vqefzxa14DKNT8jK/mV66CSjwdVQM/k3Th3EPKfUtdMniwZgMedQTFuywAsfjnZsg+pEnltaMA==", - "requires": { - "@types/node": "*" - } - }, "@types/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "dev": true }, - "@types/wif": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/wif/-/wif-2.0.1.tgz", - "integrity": "sha512-FQKvE4EncC8C4qxW9y0psAOs2XVkxSAGIofB8tqNDPSeOsR4OueEf9TED3PHf8xlOquI+m++AXTWjJIS07FNHw==", - "requires": { - "@types/node": "*" - } - }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -1227,180 +1193,6 @@ } } }, - "babel-core": { - "version": "6.26.3", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", - "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", - "requires": { - "babel-code-frame": "^6.26.0", - "babel-generator": "^6.26.0", - "babel-helpers": "^6.24.1", - "babel-messages": "^6.23.0", - "babel-register": "^6.26.0", - "babel-runtime": "^6.26.0", - "babel-template": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "convert-source-map": "^1.5.1", - "debug": "^2.6.9", - "json5": "^0.5.1", - "lodash": "^4.17.4", - "minimatch": "^3.0.4", - "path-is-absolute": "^1.0.1", - "private": "^0.1.8", - "slash": "^1.0.0", - "source-map": "^0.5.7" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "babel-generator": { - "version": "6.26.1", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", - "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", - "requires": { - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "detect-indent": "^4.0.0", - "jsesc": "^1.3.0", - "lodash": "^4.17.4", - "source-map": "^0.5.7", - "trim-right": "^1.0.1" - }, - "dependencies": { - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=" - } - } - }, - "babel-helpers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", - "requires": { - "babel-runtime": "^6.22.0", - "babel-template": "^6.24.1" - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "requires": { - "babel-runtime": "^6.22.0" - } - }, - "babel-register": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", - "requires": { - "babel-core": "^6.26.0", - "babel-runtime": "^6.26.0", - "core-js": "^2.5.0", - "home-or-tmp": "^2.0.0", - "lodash": "^4.17.4", - "mkdirp": "^0.5.1", - "source-map-support": "^0.4.15" - } - }, - "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" - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "requires": { - "babel-runtime": "^6.26.0", - "babel-traverse": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "lodash": "^4.17.4" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "requires": { - "babel-code-frame": "^6.26.0", - "babel-messages": "^6.23.0", - "babel-runtime": "^6.26.0", - "babel-types": "^6.26.0", - "babylon": "^6.18.0", - "debug": "^2.6.8", - "globals": "^9.18.0", - "invariant": "^2.2.2", - "lodash": "^4.17.4" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "requires": { - "babel-runtime": "^6.26.0", - "esutils": "^2.0.2", - "lodash": "^4.17.4", - "to-fast-properties": "^1.0.3" - }, - "dependencies": { - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" - } - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" - }, "backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", @@ -1674,105 +1466,6 @@ "safe-buffer": "^5.0.1" } }, - "bitbox-sdk": { - "version": "8.10.1", - "resolved": "https://registry.npmjs.org/bitbox-sdk/-/bitbox-sdk-8.10.1.tgz", - "integrity": "sha512-7h6JrvwGburPXIAykFpYGgVHD3hCBCEAME2C5mH9uKuCp4AbmqWYBYZhB4PPb6IAwxxXR9dtK7X3rP6PGFFEWA==", - "requires": { - "assert": "^1.4.1", - "axios": "0.19.0", - "bc-bip68": "^1.0.5", - "bigi": "^1.4.2", - "bip-schnorr": "^0.3.0", - "bip21": "github:Bitcoin-com/bip21", - "bip32-utils": "github:Bitcoin-com/bip32-utils#0.13.1", - "bip38": "^2.0.2", - "bip39": "^2.5.0", - "bip66": "^1.1.5", - "bitcoincash-ops": "github:Bitcoin-com/bitcoincash-ops#2.0.0", - "bitcoincashjs-lib": "github:Bitcoin-com/bitcoincashjs-lib#v4.0.1", - "bitcoinjs-message": "^2.0.0", - "bs58": "^4.0.1", - "cashaddrjs": "^0.2.9", - "coininfo": "github:Bitcoin-com/coininfo", - "eventsource": "^1.0.7", - "randombytes": "^2.0.6", - "safe-buffer": "^5.1.2", - "satoshi-bitcoin": "^1.0.4", - "socket.io": "^2.2.0", - "socket.io-client": "^2.2.0", - "wif": "^2.0.6" - }, - "dependencies": { - "assert": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz", - "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==", - "requires": { - "object-assign": "^4.1.1", - "util": "0.10.3" - } - }, - "bip39": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/bip39/-/bip39-2.6.0.tgz", - "integrity": "sha512-RrnQRG2EgEoqO24ea+Q/fftuPUZLmrEM3qNhhGsA3PbaXaCW791LTzPuVyx/VprXQcTbPJ3K3UeTna8ZnVl2sg==", - "requires": { - "create-hash": "^1.1.0", - "pbkdf2": "^3.0.9", - "randombytes": "^2.0.1", - "safe-buffer": "^5.0.1", - "unorm": "^1.3.3" - } - }, - "bitcoincash-ops": { - "version": "github:Bitcoin-com/bitcoincash-ops#6ab82cc7326c67236f3b2d9d0457258ac2ef48e3", - "from": "github:Bitcoin-com/bitcoincash-ops#2.0.0" - }, - "bitcoincashjs-lib": { - "version": "github:Bitcoin-com/bitcoincashjs-lib#28447b40a4ccd23913f7ade6589dc7214c99e60a", - "from": "github:Bitcoin-com/bitcoincashjs-lib#v4.0.1", - "requires": { - "bech32": "^1.1.2", - "bigi": "^1.4.0", - "bip66": "^1.1.0", - "bitcoincash-ops": "github:Bitcoin-com/bitcoincash-ops#2.0.0", - "bs58check": "^2.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.3", - "ecurve": "^1.0.0", - "merkle-lib": "^2.0.10", - "pushdata-bitcoin": "github:Bitcoin-com/pushdata-bitcoin#1.2.1", - "randombytes": "^2.0.1", - "safe-buffer": "^5.0.1", - "typeforce": "^1.11.3", - "varuint-bitcoin": "^1.0.4", - "wif": "^2.0.1" - } - }, - "cashaddrjs": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/cashaddrjs/-/cashaddrjs-0.2.9.tgz", - "integrity": "sha512-DhJF4iAH0/RM3UjHDHKRxzs09YGL9px+oTyizzydanhC7jTyM2aJ+aLKA96vZGTTWayvvr2iDF2l13lpqXiRFg==", - "requires": { - "big-integer": "^1.6.34" - } - }, - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "requires": { - "inherits": "2.0.1" - } - } - } - }, "bitcoincash-ops": { "version": "github:christroutner/bitcoincash-ops#6ab82cc7326c67236f3b2d9d0457258ac2ef48e3", "from": "github:christroutner/bitcoincash-ops" @@ -1816,45 +1509,6 @@ "varuint-bitcoin": "^1.0.1" } }, - "bitcore-lib-cash": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/bitcore-lib-cash/-/bitcore-lib-cash-0.19.0.tgz", - "integrity": "sha512-JBD30YS5M2OSUPbXggM257FjWXb5A1pHFn88lZQsqOokgddmdwp45K4APVEsj2iaKLR7uLOnGppSbAWj5Hihwg==", - "requires": { - "bn.js": "=4.11.8", - "bs58": "=4.0.1", - "buffer-compare": "=1.1.1", - "elliptic": "=6.4.0", - "inherits": "=2.0.1", - "lodash": "=4.17.11" - }, - "dependencies": { - "elliptic": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.0.tgz", - "integrity": "sha1-ysmvh2LIWDYYcAPI3+GT5eLq5d8=", - "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" - } - }, - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=" - }, - "lodash": { - "version": "4.17.11", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", - "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" - } - } - }, "bl": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", @@ -2108,11 +1762,6 @@ "ieee754": "^1.1.4" } }, - "buffer-compare": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-compare/-/buffer-compare-1.1.1.tgz", - "integrity": "sha1-W+e+hTr4kZjR9N3AkNHWakiu9ZY=" - }, "buffer-equals": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/buffer-equals/-/buffer-equals-1.0.4.tgz", @@ -2905,11 +2554,6 @@ "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "dev": true }, - "core-js": { - "version": "2.6.10", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.10.tgz", - "integrity": "sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==" - }, "core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -3271,14 +2915,6 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "requires": { - "repeating": "^2.0.0" - } - }, "diagnostics": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", @@ -4525,14 +4161,6 @@ "es5-ext": "~0.10.14" } }, - "eventsource": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-1.0.7.tgz", - "integrity": "sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ==", - "requires": { - "original": "^1.0.0" - } - }, "evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", @@ -6036,9 +5664,9 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" }, "handlebars": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.1.tgz", - "integrity": "sha512-C29UoFzHe9yM61lOsIlCE5/mQVGrnIOrOq7maQl76L7tYPCgC1og0Ajt6uWnX4ZTxBPnjw+CUvawphwCfJgUnA==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", + "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", "requires": { "neo-async": "^2.6.0", "optimist": "^0.6.1", @@ -6288,15 +5916,6 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "home-or-tmp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.1" - } - }, "hook-std": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hook-std/-/hook-std-2.0.0.tgz", @@ -6569,14 +6188,6 @@ "p-is-promise": "^3.0.0" } }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "requires": { - "loose-envify": "^1.0.0" - } - }, "ipaddr.js": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", @@ -6722,14 +6333,6 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, "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", @@ -7097,11 +6700,6 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" - }, "jsonfile": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", @@ -11721,11 +11319,6 @@ "path-key": "^2.0.0" } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, "nyc": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", @@ -12005,14 +11598,6 @@ } } }, - "original": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/original/-/original-1.0.2.tgz", - "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", - "requires": { - "url-parse": "^1.4.3" - } - }, "os-homedir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", @@ -12510,11 +12095,6 @@ "fast-diff": "^1.1.2" } }, - "private": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", - "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" - }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -12661,11 +12241,6 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.0.tgz", "integrity": "sha512-27RP4UotQORTpmNQDX8BHPukOnBP3p1uUJY5UnDhaJB+rMt9iMsok724XL+UHU23bEFOHRMQ2ZhI99qOWUMGFA==" }, - "querystringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", - "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" - }, "quick-lru": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-1.1.0.tgz", @@ -12822,11 +12397,6 @@ "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.2.0.tgz", "integrity": "sha512-LgQJIuS6nAy1Jd88DCQRemyE3mS+ispwlqMk3b0yjZ257fI1v9c+/p6SD5gP5FGyXUIgrNOAfmyioHwZtYv2VA==" }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -12892,14 +12462,6 @@ "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", "dev": true }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "requires": { - "is-finite": "^1.0.0" - } - }, "repl.history": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/repl.history/-/repl.history-0.1.4.tgz", @@ -12988,11 +12550,6 @@ } } }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" - }, "resolve": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", @@ -13646,11 +13203,6 @@ "supports-color": "^5.5.0" } }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" - }, "slice-ansi": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", @@ -13659,112 +13211,6 @@ "is-fullwidth-code-point": "^2.0.0" } }, - "slp-sdk": { - "version": "4.13.1", - "resolved": "https://registry.npmjs.org/slp-sdk/-/slp-sdk-4.13.1.tgz", - "integrity": "sha512-6ZUBbKldtoRSRvzHyDFMd4FbIQeZIymywJy/mfB+8OJiqEFxdqeqdXti9bbnIF5vT6xQjq6a45LZ6WNPUVhexg==", - "requires": { - "@types/bigi": "^1.4.2", - "@types/bip39": "^2.4.2", - "@types/randombytes": "^2.0.0", - "@types/wif": "^2.0.1", - "axios": "0.19.0", - "babel-register": "^6.26.0", - "bignumber.js": "^8.0.2", - "bitbox-sdk": "8.10.1", - "chalk": "^2.3.0", - "clear": "0.1.0", - "commander": "^2.13.0", - "figlet": "^1.2.1", - "git-clone": "^0.1.0", - "mkdirp": "^0.5.1", - "node-emoji": "^1.8.1", - "repl.history": "^0.1.4", - "slpjs": "0.21.1", - "touch": "^3.1.0" - }, - "dependencies": { - "bchaddrjs-slp": { - "version": "git://github.com/simpleledger/bchaddrjs.git#ea05d679783a6737f2f99adc37ebf485dc64f006", - "from": "git://github.com/simpleledger/bchaddrjs.git#master", - "requires": { - "bs58check": "^2.1.2", - "cashaddrjs-slp": "^0.2.11" - } - }, - "bignumber.js": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-8.1.1.tgz", - "integrity": "sha512-QD46ppGintwPGuL1KqmwhR0O+N2cZUg8JG/VzwI2e28sM9TqHjQB10lI4QAaMHVbLzwVLLAwEglpKPViWX+5NQ==" - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "slpjs": { - "version": "0.21.1", - "resolved": "https://registry.npmjs.org/slpjs/-/slpjs-0.21.1.tgz", - "integrity": "sha512-f030k+bsYOyNuuU7qmbvtQ19Xs75vkvlBZDP965j9Y9O6ehvTtiF5EDzMW5xA3Y4ur0fir01h2KbJPpEncTqqA==", - "requires": { - "@types/lodash": "^4.14.120", - "axios": "^0.18.1", - "bchaddrjs-slp": "git://github.com/simpleledger/bchaddrjs.git#master", - "bignumber.js": "^8.1.1", - "bitcore-lib-cash": "^0.19.0", - "lodash": "^4.17.14" - }, - "dependencies": { - "axios": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", - "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", - "requires": { - "follow-redirects": "1.5.10", - "is-buffer": "^2.0.2" - } - } - } - } - } - }, - "slpjs": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/slpjs/-/slpjs-0.21.4.tgz", - "integrity": "sha512-DkxuhmXD4W+UVpwnxT5NBfoc1JTwGBjADeyZZl0gPfjsWiiVJbos01z6plaiMZyetde6QPARw73jNxVs7SzW8Q==", - "requires": { - "@types/lodash": "^4.14.120", - "axios": "^0.18.1", - "bchaddrjs-slp": "git://github.com/simpleledger/bchaddrjs.git#master", - "bignumber.js": "^8.1.1", - "bitcore-lib-cash": "^0.19.0", - "lodash": "^4.17.14" - }, - "dependencies": { - "axios": { - "version": "0.18.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", - "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", - "requires": { - "follow-redirects": "1.5.10", - "is-buffer": "^2.0.2" - } - }, - "bchaddrjs-slp": { - "version": "git://github.com/simpleledger/bchaddrjs.git#ea05d679783a6737f2f99adc37ebf485dc64f006", - "from": "git://github.com/simpleledger/bchaddrjs.git#master", - "requires": { - "bs58check": "^2.1.2", - "cashaddrjs-slp": "^0.2.11" - } - }, - "bignumber.js": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-8.1.1.tgz", - "integrity": "sha512-QD46ppGintwPGuL1KqmwhR0O+N2cZUg8JG/VzwI2e28sM9TqHjQB10lI4QAaMHVbLzwVLLAwEglpKPViWX+5NQ==" - } - } - }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -13999,14 +13445,6 @@ "urix": "^0.1.0" } }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "requires": { - "source-map": "^0.5.6" - } - }, "source-map-url": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", @@ -14740,11 +14178,6 @@ "integrity": "sha1-n5up2e+odkw4dpi8v+sshI8RrbM=", "dev": true }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" - }, "triple-beam": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", @@ -14940,11 +14373,6 @@ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" }, - "unorm": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", - "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==" - }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -15041,15 +14469,6 @@ "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "dev": true }, - "url-parse": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", - "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, "url-parse-lax": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", diff --git a/package.json b/package.json index cd1070c..fec623c 100644 --- a/package.json +++ b/package.json @@ -43,8 +43,6 @@ "passport-http": "^0.3.0", "pg": "^7.11.0", "pg-hstore": "^2.3.2", - "slp-sdk": "^4.4.1", - "slpjs": "0.21.4", "strftime": "^0.10.0", "winston": "^3.2.1", "winston-daily-rotate-file": "^4.0.0" diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index d86c6dc..6f6e1af 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -13,18 +13,6 @@ const wlogger = require("../../util/winston-logging") const util = require("util") util.inspect.defaultOptions = { depth: 5 } -const SLPSDK = require("slp-sdk") -const SLP = new SLPSDK() - -// Instantiate SLPJS. -const slp = require("slpjs") -const slpjs = new slp.Slp(SLP) -const utils = slp.Utils - -// SLP tx db (LevelDB for caching) -// const level = require("level") -// const slpTxDb = level("./slp-tx-db") - // Setup JSON RPC const BitboxHTTP = axios.create({ baseURL: process.env.RPC_BASEURL diff --git a/test/v3/blockchain.js b/test/v3/blockchain.js index 7a75b08..2607e70 100644 --- a/test/v3/blockchain.js +++ b/test/v3/blockchain.js @@ -107,7 +107,7 @@ describe("#BlockchainRouter", () => { // Restore the saved URL. process.env.RPC_BASEURL = savedUrl2 - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") + assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") assert.include( result.error, "Could not communicate with full node", From a9ec0acc1a0c1d6e5a94ca2ff8cf938fa7ce6497 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 25 Nov 2019 20:47:36 -0800 Subject: [PATCH 23/49] fix(unit tests): Fixing unit tests for 503 network errors --- test/v3/blockchain.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/v3/blockchain.js b/test/v3/blockchain.js index 2607e70..503e08a 100644 --- a/test/v3/blockchain.js +++ b/test/v3/blockchain.js @@ -149,7 +149,7 @@ describe("#BlockchainRouter", () => { // Restore the saved URL. process.env.RPC_BASEURL = savedUrl2 - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") + assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") assert.include( result.error, "Could not communicate with full node", @@ -201,7 +201,7 @@ describe("#BlockchainRouter", () => { // Restore the saved URL. process.env.RPC_BASEURL = savedUrl2 - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") + assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") assert.include( result.error, "Could not communicate with full node", @@ -252,7 +252,7 @@ describe("#BlockchainRouter", () => { // Restore the saved URL. process.env.RPC_BASEURL = savedUrl2 - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") + assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") assert.include( result.error, "Network error: Could not communicate with full node", @@ -499,7 +499,7 @@ describe("#BlockchainRouter", () => { // Restore the saved URL. process.env.RPC_BASEURL = savedUrl2 - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") + assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") assert.include( result.error, "Could not communicate with full node", @@ -540,7 +540,7 @@ describe("#BlockchainRouter", () => { // Restore the saved URL. process.env.RPC_BASEURL = savedUrl2 - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") + assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") assert.include( result.error, "Could not communicate with full node", @@ -580,7 +580,7 @@ describe("#BlockchainRouter", () => { // Restore the saved URL. process.env.RPC_BASEURL = savedUrl2 - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") + assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") assert.include( result.error, "Could not communicate with full node", @@ -626,7 +626,7 @@ describe("#BlockchainRouter", () => { // Restore the saved URL. process.env.RPC_BASEURL = savedUrl2 - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") + assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") assert.include( result.error, "Could not communicate with full node", @@ -678,7 +678,7 @@ describe("#BlockchainRouter", () => { // Restore the saved URL. process.env.RPC_BASEURL = savedUrl2 - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") + assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") assert.include( result.error, "Could not communicate with full node", @@ -823,7 +823,7 @@ describe("#BlockchainRouter", () => { // Restore the saved URL. process.env.RPC_BASEURL = savedUrl2 - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") + assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") assert.include( result.error, "Could not communicate with full node", @@ -891,7 +891,7 @@ describe("#BlockchainRouter", () => { // Restore the saved URL. process.env.RPC_BASEURL = savedUrl2 - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") + assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") assert.include( result.error, "Could not communicate with full node", @@ -1028,7 +1028,7 @@ describe("#BlockchainRouter", () => { // Restore the saved URL. process.env.RPC_BASEURL = savedUrl2 - assert.equal(res.statusCode, 503, "HTTP status code 503 expected.") + assert.isAbove(res.statusCode, 499, "HTTP status code 503 expected.") assert.include( result.error, "Could not communicate with full node", From b6b8d54cdacd700cae23d54480f43cb84704ab1c Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 25 Nov 2019 20:57:02 -0800 Subject: [PATCH 24/49] Handling EAI_AGAIN error --- src/routes/v3/route-utils.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/routes/v3/route-utils.js b/src/routes/v3/route-utils.js index d49b29d..38652fc 100644 --- a/src/routes/v3/route-utils.js +++ b/src/routes/v3/route-utils.js @@ -131,6 +131,15 @@ function decodeError(err) { } } + // Different kind of network error + if (err.message && err.message.indexOf("EAI_AGAIN") > -1) { + return { + msg: + "Network error: Could not communicate with full node or other external service.", + status: 503 + } + } + return { msg: false, status: 500 } } catch (err) { wlogger.error(`unhandled error in route-utils.js/decodeError(): `, err) From c53b6e09407ddead71ffc653593477ee57fb9833 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 2 Dec 2019 19:25:10 -0800 Subject: [PATCH 25/49] fix(SLP balancesForAddress): Fixed unit tests --- src/routes/v3/slp.js | 11 +++++++---- test/v3/slp.js | 4 ++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index 6f6e1af..40357a7 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -9,6 +9,9 @@ const routeUtils = require("./route-utils") const strftime = require("strftime") const wlogger = require("../../util/winston-logging") +const BCHJS = require("@chris.troutner/bch-js") +const bchjs = new BCHJS() + // Used to convert error messages to strings, to safely pass to users. const util = require("util") util.inspect.defaultOptions = { depth: 5 } @@ -363,7 +366,7 @@ async function balancesForAddress(req, res, next) { // Ensure the input is a valid BCH address. try { - const cash = utils.toCashAddress(address) + const cash = bchjs.SLP.Address.toCashAddress(address) } catch (err) { res.status(400) return res.json({ @@ -372,7 +375,7 @@ async function balancesForAddress(req, res, next) { } // Prevent a common user error. Ensure they are using the correct network address. - const cashAddr = utils.toCashAddress(address) + const cashAddr = bchjs.SLP.Address.toCashAddress(address) const networkIsValid = routeUtils.validateNetwork(cashAddr) if (!networkIsValid) { res.status(400) @@ -386,7 +389,7 @@ async function balancesForAddress(req, res, next) { q: { db: ["a"], find: { - address: SLP.Address.toSLPAddress(address), + address: bchjs.SLP.Address.toSLPAddress(address), token_balance: { $gte: 0 } }, limit: 10000 @@ -469,7 +472,7 @@ async function balancesForAddress(req, res, next) { res.status(500) return res.json({ - error: `Error in /ddress/:address: ${err.message}` + error: `Error in /address/:address: ${err.message}` }) } } diff --git a/test/v3/slp.js b/test/v3/slp.js index 3592ae9..6a2440c 100644 --- a/test/v3/slp.js +++ b/test/v3/slp.js @@ -429,7 +429,7 @@ describe("#SLP", () => { ]) }) }) - +*/ describe("balancesForAddress()", () => { const balancesForAddress = slpRoute.testableComponents.balancesForAddress @@ -513,7 +513,7 @@ describe("#SLP", () => { ]) }) }) - + /* describe("balancesForAddressBulk()", () => { const balancesForAddressBulk = slpRoute.testableComponents.balancesForAddressBulk From 3e26fd2a8907bd48138d4c87ef9d67b876f1cc94 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 2 Dec 2019 19:31:00 -0800 Subject: [PATCH 26/49] Fixed unit and integration tests for balancesForAddress bulk and single --- src/routes/v3/slp.js | 6 +++--- test/v3/slp.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index 40357a7..4c4a4b3 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -524,7 +524,7 @@ async function balancesForAddressBulk(req, res, next) { // Ensure the input is a valid BCH address. try { - utils.toCashAddress(address) + bchjs.SLP.Address.toCashAddress(address) } catch (err) { res.status(400) return res.json({ @@ -533,7 +533,7 @@ async function balancesForAddressBulk(req, res, next) { } // Prevent a common user error. Ensure they are using the correct network address. - const cashAddr = utils.toCashAddress(address) + const cashAddr = bchjs.SLP.Address.toCashAddress(address) const networkIsValid = routeUtils.validateNetwork(cashAddr) if (!networkIsValid) { res.status(400) @@ -552,7 +552,7 @@ async function balancesForAddressBulk(req, res, next) { q: { db: ["a"], find: { - address: SLP.Address.toSLPAddress(address), + address: bchjs.SLP.Address.toSLPAddress(address), token_balance: { $gte: 0 } }, limit: 10000 diff --git a/test/v3/slp.js b/test/v3/slp.js index 6a2440c..88f70ae 100644 --- a/test/v3/slp.js +++ b/test/v3/slp.js @@ -513,7 +513,7 @@ describe("#SLP", () => { ]) }) }) - /* + describe("balancesForAddressBulk()", () => { const balancesForAddressBulk = slpRoute.testableComponents.balancesForAddressBulk @@ -599,7 +599,7 @@ describe("#SLP", () => { }) } }) - + /* describe("balancesForAddressByTokenID()", () => { const balancesForAddressByTokenID = slpRoute.testableComponents.balancesForAddressByTokenID From fb6a54f8f51fabd597ad1101ae0bb1073fc6a29e Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Wed, 4 Dec 2019 06:57:53 -0800 Subject: [PATCH 27/49] Ran npm audit fix --- package-lock.json | 38 +++++++++++++++----------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 606dc24..f5b6bb3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1969,14 +1969,6 @@ } } }, - "cashaddrjs-slp": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/cashaddrjs-slp/-/cashaddrjs-slp-0.2.11.tgz", - "integrity": "sha512-c6od4HHecD+1aEl0WmTEw+ncWBKZNqRW9Q8F+jwB1m1VozbNAmcwy0P8ihVD+cZEpwiviFyOva2simH2txINwg==", - "requires": { - "big-integer": "^1.6.34" - } - }, "chai": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", @@ -7836,9 +7828,9 @@ "dev": true }, "npm": { - "version": "6.12.1", - "resolved": "https://registry.npmjs.org/npm/-/npm-6.12.1.tgz", - "integrity": "sha512-+pMvUpgSXVBythrv//64j4i6DaLLJ1O0y8kwjNgjAE7atBNGzX4rcOEWvmsuiei6J+mA38O0nUZ/P35GuCD/jg==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.13.2.tgz", + "integrity": "sha512-TG7AFkKpjBNJh8OVJYcGaAbW0PZxEkjew51Lc6TRdhQpNjSSEnAOEpidApqEuciB7cs09C8mxbo8NbuPs4QDzg==", "dev": true, "requires": { "JSONStream": "^1.3.5", @@ -7926,14 +7918,14 @@ "once": "~1.4.0", "opener": "^1.5.1", "osenv": "^0.1.5", - "pacote": "^9.5.8", + "pacote": "^9.5.9", "path-is-inside": "~1.0.2", "promise-inflight": "~1.0.1", "qrcode-terminal": "^0.12.0", "query-string": "^6.8.2", "qw": "~1.0.1", "read": "~1.0.7", - "read-cmd-shim": "^1.0.4", + "read-cmd-shim": "^1.0.5", "read-installed": "~4.0.3", "read-package-json": "^2.1.0", "read-package-tree": "^5.3.1", @@ -9162,7 +9154,7 @@ } }, "https-proxy-agent": { - "version": "2.2.2", + "version": "2.2.4", "bundled": true, "dev": true, "requires": { @@ -9749,7 +9741,7 @@ } }, "make-fetch-happen": { - "version": "5.0.0", + "version": "5.0.2", "bundled": true, "dev": true, "requires": { @@ -9757,7 +9749,7 @@ "cacache": "^12.0.0", "http-cache-semantics": "^3.8.1", "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", + "https-proxy-agent": "^2.2.3", "lru-cache": "^5.1.1", "mississippi": "^3.0.0", "node-fetch-npm": "^2.0.2", @@ -10198,7 +10190,7 @@ } }, "pacote": { - "version": "9.5.8", + "version": "9.5.9", "bundled": true, "dev": true, "requires": { @@ -10234,7 +10226,7 @@ }, "dependencies": { "minipass": { - "version": "2.3.5", + "version": "2.9.0", "bundled": true, "dev": true, "requires": { @@ -10467,7 +10459,7 @@ } }, "read-cmd-shim": { - "version": "1.0.4", + "version": "1.0.5", "bundled": true, "dev": true, "requires": { @@ -10683,17 +10675,17 @@ "dev": true }, "smart-buffer": { - "version": "4.0.2", + "version": "4.1.0", "bundled": true, "dev": true }, "socks": { - "version": "2.3.2", + "version": "2.3.3", "bundled": true, "dev": true, "requires": { - "ip": "^1.1.5", - "smart-buffer": "4.0.2" + "ip": "1.1.5", + "smart-buffer": "^4.1.0" } }, "socks-proxy-agent": { From 0c31036940ac4b137b7e256985acd1eff0779253 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Fri, 6 Dec 2019 16:12:29 -0800 Subject: [PATCH 28/49] feat(insight): removing insight API as a downstream service --- src/app.js | 8 - src/routes/v3/bitcore.js | 1 - src/routes/v3/blockbook.js | 1 - src/routes/v3/insight/address.js | 891 ------------------------- src/routes/v3/insight/block.js | 324 --------- src/routes/v3/insight/index.js | 38 -- src/routes/v3/insight/transaction.js | 210 ------ src/routes/v3/services/bitbox.js | 11 - src/routes/v3/services/requestUtils.js | 20 - src/routes/v3/slp.js | 8 +- src/routes/v3/xpub.js | 1 - 11 files changed, 5 insertions(+), 1508 deletions(-) delete mode 100644 src/routes/v3/insight/address.js delete mode 100644 src/routes/v3/insight/block.js delete mode 100644 src/routes/v3/insight/index.js delete mode 100644 src/routes/v3/insight/transaction.js delete mode 100644 src/routes/v3/services/bitbox.js delete mode 100644 src/routes/v3/services/requestUtils.js diff --git a/src/app.js b/src/app.js index 9aa93d1..6010573 100644 --- a/src/app.js +++ b/src/app.js @@ -32,10 +32,6 @@ const slpV3 = require("./routes/v3/slp") const xpubV3 = require("./routes/v3/xpub") const bitcoreV3 = require("./routes/v3/bitcore") const blockbookV3 = require("./routes/v3/blockbook") -const insightV3 = require("./routes/v3/insight") -const insightBlockV3 = require("./routes/v3/insight/block") -const insightTranactionV3 = require("./routes/v3/insight/transaction") -const insightAddressV3 = require("./routes/v3/insight/address") require("dotenv").config() @@ -98,10 +94,6 @@ app.use(`/${v3prefix}/` + `slp`, slpV3.router) app.use(`/${v3prefix}/` + `xpub`, xpubV3.router) app.use(`/${v3prefix}/` + `bitcore`, bitcoreV3.router) app.use(`/${v3prefix}/` + `blockbook`, blockbookV3.router) -app.use(`/${v3prefix}/` + `insight`, insightV3.router) -app.use(`/${v3prefix}/` + `insight/block`, insightBlockV3.router) -app.use(`/${v3prefix}/` + `insight/transaction`, insightTranactionV3.router) -app.use(`/${v3prefix}/` + `insight/address`, insightAddressV3.router) // catch 404 and forward to error handler app.use((req, res, next) => { diff --git a/src/routes/v3/bitcore.js b/src/routes/v3/bitcore.js index 83342fd..6bccd26 100644 --- a/src/routes/v3/bitcore.js +++ b/src/routes/v3/bitcore.js @@ -5,7 +5,6 @@ "use strict" const express = require("express") -const requestUtils = require("./services/requestUtils") const axios = require("axios") const routeUtils = require("./route-utils") const wlogger = require("../../util/winston-logging") diff --git a/src/routes/v3/blockbook.js b/src/routes/v3/blockbook.js index 8b548c1..a499fc5 100644 --- a/src/routes/v3/blockbook.js +++ b/src/routes/v3/blockbook.js @@ -5,7 +5,6 @@ "use strict" const express = require("express") -const requestUtils = require("./services/requestUtils") const axios = require("axios") const routeUtils = require("./route-utils") const wlogger = require("../../util/winston-logging") diff --git a/src/routes/v3/insight/address.js b/src/routes/v3/insight/address.js deleted file mode 100644 index 9603682..0000000 --- a/src/routes/v3/insight/address.js +++ /dev/null @@ -1,891 +0,0 @@ -/* - Address route -*/ - -"use strict" - -const express = require("express") -const requestUtils = require("../services/requestUtils") -const axios = require("axios") -const routeUtils = require("../route-utils") -const wlogger = require("../../../util/winston-logging") - -//const router = express.Router() -const router = express.Router() - -// Used for processing error messages before sending them to the user. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -const BCHJS = require("@chris.troutner/bch-js") -const bchjs = new BCHJS() - -// Use the default (and max) page size of 1000 -// https://github.com/bitpay/insight-api#notes-on-upgrading-from-v03 -const PAGE_SIZE = 1000 - -// Connect the route endpoints to their handler functions. -router.get("/", root) -router.get("/details/:address", detailsSingle) -router.post("/details", detailsBulk) -router.post("/utxo", utxoBulk) -router.get("/utxo/:address", utxoSingle) -router.post("/unconfirmed", unconfirmedBulk) -router.get("/unconfirmed/:address", unconfirmedSingle) -router.get("/transactions/:address", transactionsSingle) -router.post("/transactions", transactionsBulk) -router.get("/fromXPub/:xpub", fromXPubSingle) - -// Root API endpoint. Simply acknowledges that it exists. -function root(req, res, next) { - return res.json({ status: "address" }) -} - -// Query the Insight API for details on a single BCH address. -// Returns a Promise. -async function detailsFromInsight(thisAddress, currentPage = 0) { - try { - const addr = bchjs.Address.toLegacyAddress(thisAddress) - // const addr = bchjs.Address.toCashAddress(thisAddress) - - let path = `${process.env.BITCOINCOM_BASEURL}addr/${addr}` - - // Set from and to params based on currentPage and pageSize - // https://github.com/bitpay/insight-api/blob/master/README.md#notes-on-upgrading-from-v02 - const from = currentPage * PAGE_SIZE - const to = from + PAGE_SIZE - path = `${path}?from=${from}&to=${to}` - - // console.log(`insight path: ${path}`) - - // Query the Insight server. - const axiosResponse = await axios.get(path) - const retData = axiosResponse.data - // console.log(`retData: ${util.inspect(retData)}`) - - // Calculate pagesTotal from response - const pagesTotal = Math.ceil(retData.txApperances / PAGE_SIZE) - - // Append different address formats to the return data. - retData.legacyAddress = bchjs.Address.toLegacyAddress(retData.addrStr) - retData.cashAddress = bchjs.Address.toCashAddress(retData.addrStr) - delete retData.addrStr - - // Append pagination information to the return data. - retData.currentPage = currentPage - retData.pagesTotal = pagesTotal - - return retData - } catch (err) { - // Dev Note: Do not log error messages here. Throw them instead and let the - // parent function handle it. - throw err - } -} - -// POST handler for bulk queries on address details -// curl -d '{"addresses": ["bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", "bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v2/address/details -// curl -d '{"addresses": ["bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", "bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v2/address/details -/** - * @api {post} /address/details Get Address details bulk. - * @apiName Address details bulk - * @apiGroup Address - * @apiDescription Returns the details of multiple addresses. - * - * - * @apiExample Example usage: - * curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/details - * curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/details - * - */ -async function detailsBulk(req, res, next) { - try { - let addresses = req.body.addresses - const currentPage = req.body.page ? parseInt(req.body.page, 10) : 0 - - // Reject if addresses is not an array. - if (!Array.isArray(addresses)) { - res.status(400) - return res.json({ - error: "addresses needs to be an array. Use GET for single address." - }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, addresses)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - wlogger.debug(`Executing address/details with these addresses: `, addresses) - - // Validate each element in the address array. - for (let i = 0; i < addresses.length; i++) { - const thisAddress = addresses[i] - - // Ensure the input is a valid BCH address. - try { - bchjs.Address.toLegacyAddress(thisAddress) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${thisAddress}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(thisAddress) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network for address ${thisAddress}. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - } - - // Loops through each address and creates an array of Promises, querying - // Insight API in parallel. - addresses = addresses.map(async (address, index) => - detailsFromInsight(address, currentPage) - ) - - // Wait for all parallel Insight requests to return. - const result = await axios.all(addresses) - - // Return the array of retrieved address information. - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - //logger.error(`Error in detailsBulk(): `, err) - wlogger.error(`Error in address.ts/detailsBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// GET handler for single address details -/** - * @api {get} insight/address/details/{address} Get Address details single. - * @apiName Address details single - * @apiGroup Address - * @apiDescription Returns the details of an address including balance. - * - * - * @apiExample Example usage: - * curl -X GET "https://mainnet.bchjs.cash/v3/insight/address/details/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json" - * - * - */ -async function detailsSingle(req, res, next) { - try { - const address = req.params.address - const currentPage = req.query.page ? parseInt(req.query.page, 10) : 0 - - if (!address || address === "") { - res.status(400) - return res.json({ error: "address can not be empty" }) - } - - // Reject if address is an array. - if (Array.isArray(address)) { - res.status(400) - return res.json({ - error: "address can not be an array. Use POST for bulk upload." - }) - } - - wlogger.debug( - `Executing address/detailsSingle with this address: `, - address - ) - - // Ensure the input is a valid BCH address. - try { - var legacyAddr = bchjs.Address.toLegacyAddress(address) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${address}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(address) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - - // Query the Insight API. - const retData = await detailsFromInsight(address, currentPage) - - // Return the retrieved address information. - res.status(200) - return res.json(retData) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in address.ts/detailsSingle: `, err) - wlogger.error(`Error in address.ts/detailsSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Retrieve UTXO data from the Insight API -async function utxoFromInsight(thisAddress) { - try { - const addr = bchjs.Address.toLegacyAddress(thisAddress) - // const addr = bchjs.Address.toCashAddress(thisAddress) - - const path = `${process.env.BITCOINCOM_BASEURL}addr/${addr}/utxo` - - // Query the Insight server. - const response = await axios.get(path) - - // Append different address formats to the return data. - const retData = { - utxos: Array, - legacyAddress: String, - cashAddress: String, - scriptPubKey: String - } - if (response.data.length && response.data[0].scriptPubKey) { - const spk = response.data[0].scriptPubKey - retData.scriptPubKey = spk - } - retData.legacyAddress = bchjs.Address.toLegacyAddress(thisAddress) - retData.cashAddress = bchjs.Address.toCashAddress(thisAddress) - retData.utxos = response.data.map(utxo => { - delete utxo.address - delete utxo.scriptPubKey - return utxo - }) - //console.log(`utxoFromInsight retData: ${util.inspect(retData)}`) - - return retData - } catch (err) { - // Dev Note: Do not log error messages here. Throw them instead and let the - // parent function handle it. - throw err - } -} - -/** - * @api {post} /address/utxo Get Address utxos bulk. - * @apiName Address utxos bulk. - * @apiGroup Address - * @apiDescription Returns the utxo of multiple addresses. - * - * - * @apiExample Example usage: - * curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/utxo - * curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/utxo - * - */ -// Retrieve UTXO information for an address. -async function utxoBulk(req, res, next) { - try { - let addresses = req.body.addresses - - // Reject if address is not an array. - if (!Array.isArray(addresses)) { - res.status(400) - return res.json({ error: "addresses needs to be an array" }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, addresses)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - // Validate each element in the address array. - for (let i = 0; i < addresses.length; i++) { - const thisAddress = addresses[i] - - // Ensure the input is a valid BCH address. - try { - bchjs.Address.toLegacyAddress(thisAddress) - } catch (er) { - //if (er.message.includes("Unsupported address format")) - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${thisAddress}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(thisAddress) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network for address ${thisAddress}. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - } - - wlogger.debug( - `Executing address/utxoBulk with these addresses: `, - addresses - ) - - // Loops through each address and creates an array of Promises, querying - // Insight API in parallel. - addresses = addresses.map(async (address, index) => - utxoFromInsight(address) - ) - - // Wait for all parallel Insight requests to return. - const result = await axios.all(addresses) - - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in address.ts/utxoBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// GET handler for single address details -/** - * @api {get} /insight/address/utxo/{address} Get Address utxos single. - * @apiName Address utxos single. - * @apiGroup Address - * @apiDescription Returns the list of utxo for an address. - * - * - * @apiExample Example usage: - * curl -X GET "https://mainnet.bchjs.cash/v3/insight/address/utxo/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json" - * - * - */ -async function utxoSingle(req, res, next) { - try { - const address = req.params.address - if (!address || address === "") { - res.status(400) - return res.json({ error: "address can not be empty" }) - } - - // Reject if address is an array. - if (Array.isArray(address)) { - res.status(400) - return res.json({ - error: "address can not be an array. Use POST for bulk upload." - }) - } - - wlogger.debug(`Executing address/utxoSingle with this address: `, address) - - // Ensure the input is a valid BCH address. - try { - var legacyAddr = bchjs.Address.toLegacyAddress(address) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${address}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(address) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - - // Query the Insight API. - const retData = await utxoFromInsight(address) - - // Return the array of retrieved address information. - res.status(200) - return res.json(retData) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in address.ts/utxoSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -/** - * @api {post} /address/utxo Get Unconfirmed transactions bulk. - * @apiName Unconfirmed transactions bulk - * @apiGroup Address - * @apiDescription Returns the list of unconfirmed transactions for an array of addresses. - * - * - * @apiExample Example usage: - * curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/unconfirmed - * curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/unconfirmed - * - */ -// Retrieve any unconfirmed TX information for a given address. -async function unconfirmedBulk(req, res, next) { - try { - const addresses = req.body.addresses - - // Reject if address is not an array. - if (!Array.isArray(addresses)) { - res.status(400) - return res.json({ error: "addresses needs to be an array" }) - } - - wlogger.debug(`Executing address/utxo with these addresses: `, addresses) - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, addresses)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - // Validate each element in the address array. - for (let i = 0; i < addresses.length; i++) { - const thisAddress = addresses[i] - - // Ensure the input is a valid BCH address. - try { - bchjs.Address.toLegacyAddress(thisAddress) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${thisAddress}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(thisAddress) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network for address ${thisAddress}. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - } - - // Collect an array of promises. - const promises = addresses.map(address => utxoFromInsight(address)) - - // Wait for all parallel Insight requests to return. - const result = await axios.all(promises) - - // Loop through each result - const finalResult = result.map(elem => { - //console.log(`elem: ${util.inspect(elem)}`) - - // Filter out confirmed transactions. - const unconfirmedUtxos = elem.utxos.filter( - utxo => utxo.confirmations === 0 - ) - - elem.utxos = unconfirmedUtxos - - return elem - }) - - // Return the array of retrieved address information. - res.status(200) - return res.json(finalResult) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in address.ts/unconfirmedBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -/** - * @api {get} /insight/address/utxo/{address} Get Unconfirmed transactions single. - * @apiName Unconfirmed transactions single. - * @apiGroup Address - * @apiDescription Returns the list of unconfirmed transactions for an address. - * - * - * @apiExample Example usage: - * curl -X GET "https://mainnet.bchjs.cash/v3/insight/address/unconfirmed/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json" - * - * - */ -// GET handler. Retrieve any unconfirmed TX information for a given address. -async function unconfirmedSingle(req, res, next) { - try { - const address = req.params.address - if (!address || address === "") { - res.status(400) - return res.json({ error: "address can not be empty" }) - } - - // Reject if address is an array. - if (Array.isArray(address)) { - res.status(400) - return res.json({ - error: "address can not be an array. Use POST for bulk upload." - }) - } - - wlogger.debug(`Executing address/utxoSingle with this address: `, address) - - // Ensure the input is a valid BCH address. - try { - var legacyAddr = bchjs.Address.toLegacyAddress(address) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${address}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(address) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - - // Query the Insight API. - const retData = await utxoFromInsight(address) - //console.log(`retData: ${JSON.stringify(retData,null,2)}`) - - // Loop through each returned UTXO. - const unconfirmedUTXOs = [] - for (let j = 0; j < retData.utxos.length; j++) { - const thisUtxo = retData.utxos[j] - - // Only interested in UTXOs with no confirmations. - if (thisUtxo.confirmations === 0) unconfirmedUTXOs.push(thisUtxo) - } - - retData.utxos = unconfirmedUTXOs - - // Return the array of retrieved address information. - res.status(200) - return res.json(retData) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in address.ts/unconfirmedSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Retrieve transaction data from the Insight API -async function transactionsFromInsight(thisAddress, currentPage = 0) { - try { - const path = `${process.env.BITCOINCOM_BASEURL}txs/?address=${thisAddress}&pageNum=${currentPage}` - - // Query the Insight server. - const response = await axios.get(path) - - // Append different address formats to the return data. - const retData = response.data - retData.legacyAddress = bchjs.Address.toLegacyAddress(thisAddress) - retData.cashAddress = bchjs.Address.toCashAddress(thisAddress) - retData.currentPage = currentPage - - return retData - } catch (err) { - // Dev Note: Do not log error messages here. Throw them instead and let the - // parent function handle it. - throw err - } -} -/** - * @api {post} /address/transactions Get Transactions bulk. - * @apiName Transactions bulk. - * @apiGroup Address - * @apiDescription Returns the list of transactions for an array of addresses. - * - * - * @apiExample Example usage: - * curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/transactions - * curl -d '{"addresses": ["bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c", "bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v3/insight/address/transactions - * - */ -// Get an array of TX information for a given address. -async function transactionsBulk(req, res, next) { - try { - let addresses = req.body.addresses - const currentPage = req.body.page ? parseInt(req.body.page, 10) : 0 - - // Reject if address is not an array. - if (!Array.isArray(addresses)) { - res.status(400) - return res.json({ error: "addresses needs to be an array" }) - } - - wlogger.debug(`Executing address/utxo with these addresses: `, addresses) - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, addresses)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - // Validate each element in the address array. - for (let i = 0; i < addresses.length; i++) { - const thisAddress = addresses[i] - - // Ensure the input is a valid BCH address. - try { - bchjs.Address.toLegacyAddress(thisAddress) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${thisAddress}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(thisAddress) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network for address ${thisAddress}. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - } - - // Loop through each address and collect an array of promises. - addresses = addresses.map(async (address, index) => - transactionsFromInsight(address, currentPage) - ) - - // Wait for all parallel Insight requests to return. - const result = await axios.all(addresses) - - // Return the array of retrieved address information. - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in address.ts/transactionsBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} -/** - * @api {get} /insight/address/transactions/{address} Get Transactions single. - * @apiName Transactions single. - * @apiGroup Address - * @apiDescription Returns the list of transactions for an address. - * - * - * @apiExample Example usage: - * curl -X GET "https://mainnet.bchjs.cash/v3/insight/address/transactions/bitcoincash:qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c" -H "accept: application/json" - * - * - */ -// GET handler. Retrieve any unconfirmed TX information for a given address. -async function transactionsSingle(req, res, next) { - try { - const address = req.params.address - const currentPage = req.query.page ? parseInt(req.query.page, 10) : 0 - - if (!address || address === "") { - res.status(400) - return res.json({ error: "address can not be empty" }) - } - - // Reject if address is an array. - if (Array.isArray(address)) { - res.status(400) - return res.json({ - error: "address can not be an array. Use POST for bulk upload." - }) - } - - wlogger.debug( - `Executing address/transactionsSingle with this address: `, - address - ) - - // Ensure the input is a valid BCH address. - try { - var legacyAddr = bchjs.Address.toLegacyAddress(address) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${address}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(address) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - - // Query the Insight API. - const retData = await transactionsFromInsight(address, currentPage) - //console.log(`retData: ${JSON.stringify(retData,null,2)}`) - - // Return the array of retrieved address information. - res.status(200) - return res.json(retData) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in address.ts/transactionsSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} -/** - * @api {get} /insight/address/fromXPub/{xpub} Get FromXPub - * @apiName FromXPub - * @apiGroup Address - * @apiDescription Returns an address for an extended public key and hdpath. - * - * - * @apiExample Example usage: - * curl -X GET "https://mainnet.bchjs.cash/v3/insight/address/fromXPub/xpub661MyMwAqRbcG4CnhNYoK1r1TKLwQQ1UdC3LHoWFK61rsnzh7Hx35qQ9Z53ucYcE5WvA7GEDXhqqKjSY2e6Y8n7WNVLYHpXCuuX945VPuYn" -H "accept: application/json" - * - * - */ -async function fromXPubSingle(req, res, next) { - try { - const xpub = req.params.xpub - const hdPath = req.query.hdPath ? req.query.hdPath : "0" - - if (!xpub || xpub === "") { - res.status(400) - return res.json({ error: "xpub can not be empty" }) - } - - // Reject if xpub is an array. - if (Array.isArray(xpub)) { - res.status(400) - return res.json({ - error: "xpub can not be an array. Use POST for bulk upload." - }) - } - - wlogger.debug(`Executing address/fromXPub with this xpub: `, xpub) - - const cashAddr = bchjs.Address.fromXPub(xpub, hdPath) - const legacyAddr = bchjs.Address.toLegacyAddress(cashAddr) - res.status(200) - return res.json({ - cashAddress: cashAddr, - legacyAddress: legacyAddr - }) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in address.ts/fromXPubSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -module.exports = { - router, - testableComponents: { - root, - detailsBulk, - detailsSingle, - utxoBulk, - utxoSingle, - unconfirmedBulk, - unconfirmedSingle, - transactionsBulk, - transactionsSingle, - fromXPubSingle - } -} diff --git a/src/routes/v3/insight/block.js b/src/routes/v3/insight/block.js deleted file mode 100644 index d813806..0000000 --- a/src/routes/v3/insight/block.js +++ /dev/null @@ -1,324 +0,0 @@ -"use strict" - -const express = require("express") -const requestUtils = require("../services/requestUtils") -const axios = require("axios") -const bitbox = require("../services/bitbox") -const wlogger = require("../../../util/winston-logging") - -const routeUtils = require("../route-utils") - -// Used for processing error messages before sending them to the user. -const util = require("util") -util.inspect.defaultOptions = { depth: 3 } - -const router = express.Router() -//const BitboxHTTP = bitbox.getInstance() - -router.get("/", root) -router.get("/detailsByHash/:hash", detailsByHashSingle) -router.post("/detailsByHash", detailsByHashBulk) -router.get("/detailsByHeight/:height", detailsByHeightSingle) -router.post("/detailsByHeight", detailsByHeightBulk) - -function root(req, res, next) { - return res.json({ status: "block" }) -} -/** - * @api {get} /block/detailsByHash/{hash} Get Block details single. - * @apiName Block details single - * @apiGroup Block - * @apiDescription Returns details about a single block by hash. - * - * - * @apiExample Example usage: - * curl -X GET "https://mainnet.bchjs.cash/v3/insight/block/detailsByHash/000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201" -H "accept: application/json" - * - * - */ -// Call the insight server to get block details based on the hash. -async function detailsByHashSingle(req, res, next) { - try { - const hash = req.params.hash - - // Reject if hash is empty - if (!hash || hash === "") { - res.status(400) - return res.json({ error: "hash must not be empty" }) - } - - const response = await axios.get( - `${process.env.BITCOINCOM_BASEURL}block/${hash}` - ) - //console.log(`response.data: ${JSON.stringify(response.data,null,2)}`) - - const parsed = response.data - return res.json(parsed) - } catch (error) { - //console.log(`error object: ${util.inspect(error)}`) - - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(error) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - if (error.response && error.response.status === 404) { - res.status(404) - return res.json({ error: "Not Found" }) - } - - // Write out error to error log. - //logger.error(`Error in block/detailsByHash: `, error) - wlogger.error(`Error in block.ts/detailsByHashSingle().`, error) - - res.status(500) - return res.json({ error: util.inspect(error) }) - } -} -/** - * @api {post} /block/detailsByHash Get Block details bulk. - * @apiName Block details bulk - * @apiGroup Block - * @apiDescription Returns retails about multiple blocks by hashes. - * - * - * @apiExample Example usage: - * curl -d '{"hashes":["0000000000000000040e83398a79a16390897f0d18c92bada6350a19a32ec984","000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201"]}' -H "Content-Type: application/json" "https://mainnet.bchjs.cash/v3/insight/block/detailsByHash" - * curl -d '{"hashes":["0000000000000000040e83398a79a16390897f0d18c92bada6350a19a32ec984","000000000000000005e14d3f9fdfb70745308706615cfa9edca4f4558332b201"], "from": 1, "to": 5}' -H "Content-Type: application/json" "https://mainnet.bchjs.cash/v3/insight/block/detailsByHash" - * - */ -async function detailsByHashBulk(req, res, next) { - try { - const hashes = req.body.hashes - //console.log(`hashes: ${JSON.stringify(hashes, null, 2)}`) - - // Reject if hashes is not an array. - if (!Array.isArray(hashes)) { - res.status(400) - return res.json({ - error: "hashes needs to be an array. Use GET for single address." - }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, hashes)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - // Validate each hash in the array. - for (let i = 0; i < hashes.length; i++) { - const thisHash = hashes[i] - - if (thisHash.length !== 64) { - res.status(400) - return res.json({ - error: `Invalid hash. Double check your hash is valid: ${thisHash}` - }) - } - } - - // Loop through each hash and creates an array of promises - const axiosPromises = hashes.map(async hash => - axios.get(`${process.env.BITCOINCOM_BASEURL}block/${hash}`) - ) - - // Wait for all parallel promises to return. - const axiosResult = await axios.all(axiosPromises) - - // Extract the data component from the axios response. - const result = axiosResult.map(x => x.data) - //console.log(`result: ${util.inspect(result)}`) - - res.status(200) - return res.json(result) - } catch (error) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(error) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - if (error.response && error.response.status === 404) { - res.status(404) - return res.json({ error: "Not Found" }) - } - - // Write out error to error log. - //logger.error(`Error in block/detailsByHash: `, error) - wlogger.error(`Error in block.ts/detailsByHashBulk().`, error) - - res.status(500) - return res.json({ error: util.inspect(error) }) - } -} -/** - * @api {get} /block/detailsByHeight/{hash} Get Block details single by height. - * @apiName Block details single by height - * @apiGroup Block - * @apiDescription Returns Details about a single block by height. - * - * - * @apiExample Example usage: - * curl -X GET "https://mainnet.bchjs.cash/v3/insight/block/block/detailsByHeight/500000" -H "accept: application/json" - * - * - */ -// Call the Full Node to get block hash based on height, then call the Insight -// server to get details from that hash. -async function detailsByHeightSingle(req, res, next) { - try { - const height = req.params.height - - // Reject if id is empty - if (!height || height === "") { - res.status(400) - return res.json({ error: "height must not be empty" }) - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - requestConfig.data.id = "getblockhash" - requestConfig.data.method = "getblockhash" - requestConfig.data.params = [parseInt(height)] - - const response = await BitboxHTTP(requestConfig) - - const hash = response.data.result - //console.log(`response.data: ${util.inspect(response.data)}`) - - // Call detailsByHashSingle now that the hash has been retrieved. - req.params.hash = hash - return detailsByHashSingle(req, res, next) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in control/getInfo: `, error) - wlogger.error(`Error in block.ts/detailsByHeightSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} -/** - * @api {post} /block/detailsByHash Get Block details bulk by height. - * @apiName Block details bulk by height - * @apiGroup Block - * @apiDescription Returns Bulk details about blocks by heights. - * - * - * @apiExample Example usage: - * curl -d '{"heights":[499000,500000]}' -H "Content-Type: application/json" "https://mainnet.bchjs.cash/v3/insight/block/detailsByHeight" - * curl -d '{"heights":[499000,500000], "from": 1, "to": 5}' -H "Content-Type: application/json" "https://mainnet.bchjs.cash/v3/insight/block/detailsByHeight" - * - */ -async function detailsByHeightBulk(req, res, next) { - try { - const heights = req.body.heights - - // Reject if heights is not an array. - if (!Array.isArray(heights)) { - res.status(400) - return res.json({ - error: "heights needs to be an array. Use GET for single height." - }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, heights)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - wlogger.debug(`Executing detailsByHeight with these heights: `, heights) - - // Validate each element in the address array. - for (let i = 0; i < heights.length; i++) { - const thisHeight = heights[i] - - // Reject if id is empty - if (!thisHeight || thisHeight === "") { - res.status(400) - return res.json({ error: "height must not be empty" }) - } - } - - const { - BitboxHTTP, - username, - password, - requestConfig - } = routeUtils.setEnvVars() - - // Loop through each height and creates an array of requests to call in parallel - const promises = heights.map(async height => { - requestConfig.data.id = "getblockhash" - requestConfig.data.method = "getblockhash" - requestConfig.data.params = [parseInt(height)] - - const response = await BitboxHTTP(requestConfig) - - const hash = response.data.result - - const axiosResult = await axios.get( - `${process.env.BITCOINCOM_BASEURL}block/${hash}` - ) - - return axiosResult.data - }) - - // Wait for all parallel Insight requests to return. - const result = await axios.all(promises) - - res.status(200) - return res.json(result) - } catch (error) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(error) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - if (error.response && error.response.status === 404) { - res.status(404) - return res.json({ error: "Not Found" }) - } - - // Write out error to error log. - //logger.error(`Error in block/detailsByHash: `, error) - wlogger.error(`Error in block.ts/detailsByHeightBulk().`, error) - - res.status(500) - return res.json({ error: util.inspect(error) }) - } -} - -module.exports = { - router, - testableComponents: { - root, - detailsByHashSingle, - detailsByHashBulk, - detailsByHeightSingle, - detailsByHeightBulk - } -} diff --git a/src/routes/v3/insight/index.js b/src/routes/v3/insight/index.js deleted file mode 100644 index 8f3c11f..0000000 --- a/src/routes/v3/insight/index.js +++ /dev/null @@ -1,38 +0,0 @@ -/* - Insight API route - A parent route that encompases the other libraries. -*/ - -"use strict" - -const express = require("express") -const requestUtils = require("../services/requestUtils") -const axios = require("axios") -const routeUtils = require("../route-utils") -const wlogger = require("../../../util/winston-logging") - -const router = express.Router() - -// Used for processing error messages before sending them to the user. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -//const BCHJS = require("@chris.troutner/bch-js") -//const bchjs = new BCHJS() - -//const BITCORE_URL = process.env.BITCORE_URL - -// Connect the route endpoints to their handler functions. -router.get("/", root) - -// Root API endpoint. Simply acknowledges that it exists. -function root(req, res, next) { - return res.json({ status: "address" }) -} - -module.exports = { - router, - testableComponents: { - root - } -} diff --git a/src/routes/v3/insight/transaction.js b/src/routes/v3/insight/transaction.js deleted file mode 100644 index ad6cc29..0000000 --- a/src/routes/v3/insight/transaction.js +++ /dev/null @@ -1,210 +0,0 @@ -"use strict" - -const express = require("express") -const router = express.Router() -const axios = require("axios") - -const routeUtils = require("../route-utils") -const wlogger = require("../../../util/winston-logging") - -const BCHJS = require("@chris.troutner/bch-js") -const bchjs = new BCHJS() - -// Used to convert error messages to strings, to safely pass to users. -const util = require("util") -util.inspect.defaultOptions = { depth: 3 } - -// Manipulates and formats the raw data comming from Insight API. -const processInputs = tx => { - // Add legacy and cashaddr to tx vin - if (tx.vin) { - tx.vin.forEach(vin => { - if (!vin.coinbase) { - vin.value = vin.valueSat - const address = vin.addr - if (address) { - vin.legacyAddress = bchjs.Address.toLegacyAddress(address) - vin.cashAddress = bchjs.Address.toCashAddress(address) - delete vin.addr - } - delete vin.valueSat - delete vin.doubleSpentTxID - } - }) - } - - // Add legacy and cashaddr to tx vout - if (tx.vout) { - tx.vout.forEach(vout => { - // Overwrite value string with value in satoshis - //vout.value = parseFloat(vout.value) * 100000000 - - if (vout.scriptPubKey) { - if (vout.scriptPubKey.addresses) { - const cashAddrs = [] - vout.scriptPubKey.addresses.forEach(addr => { - const cashAddr = bchjs.Address.toCashAddress(addr) - cashAddrs.push(cashAddr) - }) - vout.scriptPubKey.cashAddrs = cashAddrs - } - } - }) - } -} - -router.get("/", root) -router.post("/details", detailsBulk) -router.get("/details/:txid", detailsSingle) - -function root(req, res, next) { - return res.json({ status: "transaction" }) -} - -// Retrieve transaction data from the Insight API -// This function is also used by the SLP route library. -async function transactionsFromInsight(txid) { - try { - const path = `${process.env.BITCOINCOM_BASEURL}tx/${txid}` - - // Query the Insight server. - const response = await axios.get(path) - //console.log(`Insight output: ${JSON.stringify(response.data, null, 2)}`) - - // Parse the data. - const parsed = response.data - if (parsed) processInputs(parsed) - - return parsed - } catch (err) { - // Dev Note: Do not log error messages here. Throw them instead and let the - // parent function handle it. - throw err - } -} -/** - * @api {post} /transaction/details Get Transaction details bulk. - * @apiName Transaction details bulk - * @apiGroup Transaction - * @apiDescription Returns Bulk Details about a transaction. - * - * - * @apiExample Example usage: - * curl -X POST "https://mainnet.bchjs.cash/v3/insight/transaction/details" -H "Content-Type: application/json" -d "{"txids":["a5f972572ee1753e2fd2457dd61ce5f40fa2f8a30173d417e49feef7542c96a1","5165dc531aad05d1149bb0f0d9b7bda99c73e2f05e314bcfb5b4bb9ca5e1af5e"]}" - * - */ - -async function detailsBulk(req, res, next) { - try { - const txids = req.body.txids - - // Reject if address is not an array. - if (!Array.isArray(txids)) { - res.status(400) - return res.json({ error: "txids needs to be an array" }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, txids)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - wlogger.debug(`Executing transaction/details with these txids: `, txids) - - // Collect an array of promises - const promises = txids.map( - async txid => await transactionsFromInsight(txid) - ) - - // Wait for all parallel promises to return. - const result = await Promise.all(promises) - - // Return the array of retrieved transaction information. - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - wlogger.error(`Error in transactions.ts/detailsBulk().`, err) - - //console.log(`Error in transaction details: `, err) - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// GET handler. Retrieve any unconfirmed TX information for a given address. -/** - * @api {get} insight/transaction/details/{txid} Get Transaction details single. - * @apiName Transaction details single - * @apiGroup Transaction - * @apiDescription Returns the details about a single transaction. - * - * - * @apiExample Example usage: - * curl -X GET "https://mainnet.bchjs.cash/v3/insight/transaction/details/fe28050b93faea61fa88c4c630f0e1f0a1c24d0082dd0e10d369e13212128f33" -H "accept: application/json" - * - * - */ -async function detailsSingle(req, res, next) { - try { - const txid = req.params.txid - if (!txid || txid === "") { - res.status(400) - return res.json({ error: "txid can not be empty" }) - } - - // Reject if address is an array. - if (Array.isArray(txid)) { - res.status(400) - return res.json({ - error: "txid can not be an array. Use POST for bulk upload." - }) - } - - wlogger.debug( - `Executing transaction.ts/detailsSingle with this txid: `, - txid - ) - - // Query the Insight API. - const retData = await transactionsFromInsight(txid) - //console.log(`retData: ${JSON.stringify(retData,null,2)}`) - - // Return the array of retrieved address information. - res.status(200) - return res.json(retData) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - //logger.error(`Error in rawtransactions/decodeRawTransaction: `, err) - wlogger.error(`Error in transactions.ts/detailsSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -module.exports = { - router, - transactionsFromInsight, - testableComponents: { - root, - detailsBulk, - detailsSingle - } -} diff --git a/src/routes/v3/services/bitbox.js b/src/routes/v3/services/bitbox.js deleted file mode 100644 index 8fb0414..0000000 --- a/src/routes/v3/services/bitbox.js +++ /dev/null @@ -1,11 +0,0 @@ -"use strict" - -const axios = require("axios") - -const BitboxHTTP = axios.create({ - baseURL: process.env.RPC_BASEURL -}) - -const getInstance = () => BitboxHTTP - -module.exports = getInstance diff --git a/src/routes/v3/services/requestUtils.js b/src/routes/v3/services/requestUtils.js deleted file mode 100644 index 7f64965..0000000 --- a/src/routes/v3/services/requestUtils.js +++ /dev/null @@ -1,20 +0,0 @@ -"use strict" - -const username = process.env.RPC_USERNAME -const password = process.env.RPC_PASSWORD - -const getRequestConfig = (method, params) => ({ - method: "post", - auth: { - username, - password - }, - data: { - jsonrpc: "1.0", - id: method, - method, - params - } -}) - -module.exports = getRequestConfig diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index 4c4a4b3..c3eb6eb 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -27,7 +27,8 @@ const password = process.env.RPC_PASSWORD // https://gist.github.com/christroutner/fc717ca704dec3dded8b52fae387eab2 const SLPDB_PASS = process.env.SLPDB_PASS ? process.env.SLPDB_PASS : "BITBOX" -const transactions = require("./insight/transaction") +// const transactions = require("./insight/transaction") +const rawtransactions = require("./full-node/rawtransactions") // Setup REST and TREST URLs used by slpjs // Dev note: this allows for unit tests to mock the URL. @@ -1201,8 +1202,9 @@ async function txDetails(req, res, next) { // console.log(`formatted: ${JSON.stringify(formatted,null,2)}`) // Get information on the transaction from Insight API. - const retData = await transactions.transactionsFromInsight(txid) - // console.log(`retData: ${JSON.stringify(retData,null,2)}`) + // const retData = await transactions.transactionsFromInsight(txid) + const retData = await rawtransactions.getRawTransactionsFromNode(txid, true) + // console.log(`retData: ${JSON.stringify(retData, null, 2)}`) // Return both the tx data from Insight and the formatted token information. const response = { diff --git a/src/routes/v3/xpub.js b/src/routes/v3/xpub.js index d7da89a..142a75e 100644 --- a/src/routes/v3/xpub.js +++ b/src/routes/v3/xpub.js @@ -5,7 +5,6 @@ "use strict" const express = require("express") -const requestUtils = require("./services/requestUtils") const axios = require("axios") const routeUtils = require("./route-utils") const wlogger = require("../../util/winston-logging") From 098f5214e0d11e191dee90751d825ebe2c7f3498 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Fri, 6 Dec 2019 16:15:27 -0800 Subject: [PATCH 29/49] Fixed tests --- test/v3/block.js | 611 ---------------- test/v3/insight-address.js | 1348 ------------------------------------ test/v3/transaction.js | 333 --------- 3 files changed, 2292 deletions(-) delete mode 100644 test/v3/block.js delete mode 100644 test/v3/insight-address.js delete mode 100644 test/v3/transaction.js diff --git a/test/v3/block.js b/test/v3/block.js deleted file mode 100644 index 26477f4..0000000 --- a/test/v3/block.js +++ /dev/null @@ -1,611 +0,0 @@ -"use strict" - -const blockRoute = require("../../src/routes/v3/insight/block") -const chai = require("chai") -const assert = chai.assert -const nock = require("nock") // HTTP mocking - -let originalEnvVars // Used during transition from integration to unit tests. - -// Mocking data. -const { mockReq, mockRes } = require("./mocks/express-mocks") -const mockData = require("./mocks/block-mock") - -// Used for debugging. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -describe("#Block", () => { - let req, res - - before(() => { - // Save existing environment variables. - originalEnvVars = { - BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL, - RPC_BASEURL: process.env.RPC_BASEURL, - RPC_USERNAME: process.env.RPC_USERNAME, - RPC_PASSWORD: process.env.RPC_PASSWORD - } - - // Set default environment variables for unit tests. - if (!process.env.TEST) process.env.TEST = "unit" - if (process.env.TEST === "unit") { - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - process.env.RPC_BASEURL = "http://fakeurl/api" - process.env.RPC_USERNAME = "fakeusername" - process.env.RPC_PASSWORD = "fakepassword" - } - }) - - // Setup the mocks before each test. - beforeEach(() => { - // Mock the req and res objects used by Express routes. - req = mockReq - res = mockRes - - // Explicitly reset the parmas and body. - req.params = {} - req.body = {} - - // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() - }) - - afterEach(() => { - // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() - }) - - after(() => { - // Restore any pre-existing environment variables. - process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL - process.env.RPC_BASEURL = originalEnvVars.RPC_BASEURL - process.env.RPC_USERNAME = originalEnvVars.RPC_USERNAME - process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD - }) - - describe("#root", () => { - // root route handler. - const root = blockRoute.testableComponents.root - - it("should respond to GET for base route", async () => { - const result = root(req, res) - - assert.equal(result.status, "block", "Returns static string") - }) - }) - - describe("#detailsByHashSingle", () => { - const detailsByHash = blockRoute.testableComponents.detailsByHashSingle - - it("should throw an error for an empty hash", async () => { - req.params.hash = "" - - const result = await detailsByHash(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "hash must not be empty", - "Proper error message" - ) - }) - - it("should throw 50X when network issues", async () => { - // Save the existing RPC URL. - const savedUrl = process.env.BITCOINCOM_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - req.params.hash = "abc123" - const result = await detailsByHash(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.isAbove(res.statusCode, 499, "HTTP status code 50X expected.") - //assert.include(result.error, "ENOTFOUND", "Error message expected") - }) - - it("should throw an error for invalid hash", async () => { - req.params.hash = "abc123" - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/block/${req.params.hash}`) - .reply(404, "Not found") - } - - const result = await detailsByHash(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.include(result.error, "Not found", "Proper error message") - }) - - it("should GET /detailsByHash/:hash", async () => { - req.params.hash = - "0000000000000000009ff8b38118d540e33c262bf6f78fc6863cc444526dc086" - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/block/${req.params.hash}`) - .reply(200, mockData.mockBlockDetails) - } - - const result = await detailsByHash(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAnyKeys(result, [ - "hash", - "size", - "height", - "version", - "merkleroot", - "tx", - "time", - "nonce", - "bits", - "difficulty", - "chainwork", - "confirmations", - "previousblockhash", - "nextblockhash", - "reward", - "isMainChain", - "poolInfo" - ]) - assert.isArray(result.tx) - }) - }) - - describe("#detailsByHashBulk", () => { - // details route handler. - const detailsByHashBulk = blockRoute.testableComponents.detailsByHashBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await detailsByHashBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "hashes needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single address", async () => { - req.body = { - hashes: - "0000000000000000009ff8b38118d540e33c262bf6f78fc6863cc444526dc086" - } - - const result = await detailsByHashBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "hashes needs to be an array", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.hashes = testArray - - const result = await detailsByHashBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should throw an error for an invalid hash", async () => { - req.body = { - hashes: [`abc123`] - } - - const result = await detailsByHashBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid hash", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.body = { - hashes: [ - "00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79" - ] - } - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await detailsByHashBulk(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get details for a single hash", async () => { - req.body = { - hashes: [ - "0000000000000000009ff8b38118d540e33c262bf6f78fc6863cc444526dc086" - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/block/${req.body.hashes[0]}`) - .reply(200, mockData.mockBlockDetails) - } - - // Call the details API. - const result = await detailsByHashBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert that required fields exist in the returned object. - assert.equal(result.length, 1, "Array with one entry") - assert.hasAllKeys(result[0], [ - "bits", - "chainwork", - "confirmations", - "difficulty", - "hash", - "height", - "isMainChain", - "merkleroot", - "nextblockhash", - "nonce", - "poolInfo", - "previousblockhash", - "reward", - "size", - "time", - "tx", - "version" - ]) - }) - - it("should get details for multiple hashes", async () => { - req.body = { - hashes: [ - `0000000000000000009ff8b38118d540e33c262bf6f78fc6863cc444526dc086`, - `0000000000000000009ff8b38118d540e33c262bf6f78fc6863cc444526dc086` - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/block/${req.body.hashes[0]}`) - .reply(200, mockData.mockBlockDetails) - - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/block/${req.body.hashes[1]}`) - .reply(200, mockData.mockBlockDetails) - } - - // Call the details API. - const result = await detailsByHashBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.equal(result.length, 2, "2 outputs for 2 inputs") - }) - - it("should throw an error if hash not found", async () => { - req.body = { - hashes: [ - `00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44abcdef` - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/block/${req.body.hashes[0]}`) - //.reply(404, { error: { message: "Not Found" } }) - .reply(404, "Not found") - } - - const result = await detailsByHashBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 404, "HTTP status code 404 expected.") - assert.include(result.error, "Not found", "Proper error message") - }) - }) - - describe("Block Details By Height", () => { - // block route handler. - const detailsByHeight = blockRoute.testableComponents.detailsByHeightSingle - - it("should throw an error for an empty height", async () => { - req.params.height = "" - - const result = await detailsByHeight(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "height must not be empty", - "Proper error message" - ) - }) - - it("should throw 500 when network issues", async () => { - // Save the existing RPC URL. - const savedUrl = process.env.BITCOINCOM_BASEURL - const savedUrl2 = process.env.RPC_BASEURL - - // Manipulate the URL to cause a 500 network error. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - process.env.RPC_BASEURL = "http://fakeurl/api/" - - req.params.height = "abc123" - const result = await detailsByHeight(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - process.env.RPC_BASEURL = savedUrl2 - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or great expected." - ) - //assert.include(result.error, "ENOTFOUND", "Error message expected") - }) - - it("should throw an error for invalid height", async () => { - req.params.height = "abc123" - - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - console.log(`process.env.RPC_BASEURL: ${process.env.RPC_BASEURL}`) - nock(`${process.env.RPC_BASEURL}`) - .post(uri => uri.includes("/")) - .reply(500, { - error: { - code: -1, - message: "JSON value is not an integer as expected" - } - }) - } - - const result = await detailsByHeight(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "JSON value is not an integer as expected", - "Proper error message" - ) - }) - - it("should GET /detailsByHeight/:height", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(uri => uri.includes("/")) - .reply(200, { result: mockData.mockBlockHash }) - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get( - `/block/00000000000000645dec6503d3f5eafb0d2537a7a28f181d721dec7c44154c79` - ) - .reply(200, mockData.mockBlockDetails) - } - - req.params.height = 500000 - - const result = await detailsByHeight(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAnyKeys(result, [ - "hash", - "size", - "height", - "version", - "merkleroot", - "tx", - "time", - "nonce", - "bits", - "difficulty", - "chainwork", - "confirmations", - "previousblockhash", - "nextblockhash", - "reward", - "isMainChain", - "poolInfo" - ]) - assert.isArray(result.tx) - }) - }) - - describe("#detailsByHeightBulk", () => { - // details route handler. - const detailsByHeightBulk = - blockRoute.testableComponents.detailsByHeightBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await detailsByHeightBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "heights needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single height", async () => { - req.body = { - heights: 500000 - } - - const result = await detailsByHeightBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "heights needs to be an array", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.heights = testArray - - const result = await detailsByHeightBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should throw error for an invalid height", async () => { - // Mock the RPC call for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(uri => uri.includes("/")) - .reply(500, { - error: { - code: -1, - message: "JSON value is not an integer as expected" - } - }) - } - - req.body.heights = [`abc123`] - - const result = await detailsByHeightBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.body.heights = [`500000`] - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await detailsByHeightBulk(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get details for a single height", async () => { - req.body.heights = [`500000`] - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(uri => uri.includes("/")) - .reply(200, { result: mockData.mockBlockHash }) - - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/block/${mockData.mockBlockHash}`) - .reply(200, mockData.mockBlockDetails) - } - - // Call the details API. - const result = await detailsByHeightBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert that required fields exist in the returned object. - assert.equal(result.length, 1, "Array with one entry") - assert.hasAllKeys(result[0], [ - "bits", - "chainwork", - "confirmations", - "difficulty", - "hash", - "height", - "isMainChain", - "merkleroot", - "nextblockhash", - "nonce", - "poolInfo", - "previousblockhash", - "reward", - "size", - "time", - "tx", - "version" - ]) - }) - - it("should get details for multiple block heights", async () => { - req.body = { - heights: [`500000`, `500001`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.RPC_BASEURL}`) - .post(uri => uri.includes("/")) - .times(2) - .reply(200, { result: mockData.mockBlockHash }) - - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/block/${mockData.mockBlockHash}`) - .times(2) - .reply(200, mockData.mockBlockDetails) - } - - // Call the details API. - const result = await detailsByHeightBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.equal(result.length, 2, "2 outputs for 2 inputs") - }) - }) -}) diff --git a/test/v3/insight-address.js b/test/v3/insight-address.js deleted file mode 100644 index 992141b..0000000 --- a/test/v3/insight-address.js +++ /dev/null @@ -1,1348 +0,0 @@ -/* - TESTS FOR THE ADDRESS.JS LIBRARY - - This test file uses the environment variable TEST to switch between unit - and integration tests. By default, TEST is set to 'unit'. Set this variable - to 'integration' to run the tests against BCH mainnet. - - To-Do: - -/details/:address - --Verify to/from query options work correctly. - -GET /unconfirmed/:address & POST /unconfirmed - --Should initiate a transfer of BCH to verify unconfirmed TX. - ---This would be more of an e2e test. -*/ - -"use strict" - -const chai = require("chai") -const assert = chai.assert -const addressRoute = require("../../src/routes/v3/insight/address") -const nock = require("nock") // HTTP mocking - -let originalUrl // Used during transition from integration to unit tests. - -// Mocking data. -const { mockReq, mockRes } = require("./mocks/express-mocks") -const mockData = require("./mocks/address-mock") - -// Used for debugging. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -describe("#Insight Address", () => { - let req, res - - before(() => { - originalUrl = process.env.BITCOINCOM_BASEURL - - // Set default environment variables for unit tests. - if (!process.env.TEST) process.env.TEST = "unit" - if (process.env.TEST === "unit") - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - // console.log(`Testing type is: ${process.env.TEST}`) - }) - - // Setup the mocks before each test. - beforeEach(() => { - // Mock the req and res objects used by Express routes. - req = mockReq - res = mockRes - - // Explicitly reset the parmas and body. - req.params = {} - req.body = {} - req.query = {} - - // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() - }) - - afterEach(() => { - // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() - }) - - after(() => { - process.env.BITCOINCOM_BASEURL = originalUrl - }) - - describe("#root", () => { - // root route handler. - const root = addressRoute.testableComponents.root - - it("should respond to GET for base route", async () => { - const result = root(req, res) - - assert.equal(result.status, "address", "Returns static string") - }) - }) - - describe("#AddressDetailsBulk", () => { - // details route handler. - const detailsBulk = addressRoute.testableComponents.detailsBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single address", async () => { - req.body = { - address: `qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - } - - const result = await detailsBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid address", async () => { - req.body = { - addresses: [`02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - } - - const result = await detailsBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.addresses = testArray - - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should detect a network mismatch", async () => { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await detailsBulk(req, res) - //console.log(`network issue result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.isAbove(res.statusCode, 499, "HTTP status code 500 expected.") - //assert.include(result.error, "ENOTFOUND", "Error message expected") - assert.include( - result.error, - "Network error: Could not communicate", - "Error message expected" - ) - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should default to page 0", async () => { - req.body = { - addresses: [`bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - // Assert current page defaults to 0 - assert.equal(result[0].currentPage, 0) - }) - - it("should process the requested page", async () => { - req.body = { - addresses: [`bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7`], - page: 5 - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert current page is same as requested - assert.equal(result[0].currentPage, 5) - }) - - it("should calculate the total number of pages", async () => { - req.body = { - addresses: [`bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(result[0].pagesTotal, 1) - }) - - it("should get details for a single address", async () => { - req.body = { - addresses: [`bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - // Assert that required fields exist in the returned object. - assert.equal(result.length, 1, "Array with one entry") - assert.hasAllKeys(result[0], [ - "balance", - "balanceSat", - "totalReceived", - "totalReceivedSat", - "totalSent", - "totalSentSat", - "unconfirmedBalance", - "unconfirmedBalanceSat", - "unconfirmedTxApperances", - "txApperances", - "transactions", - "legacyAddress", - "cashAddress", - "currentPage", - "pagesTotal" - ]) - }) - - it("should get details for multiple addresses", async () => { - req.body = { - addresses: [ - `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7`, - `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7` - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .times(2) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.equal(result.length, 2, "2 outputs for 2 inputs") - }) - }) - - describe("#AddressDetailsSingle", () => { - // details route handler. - const detailsSingle = addressRoute.testableComponents.detailsSingle - - it("should throw 400 if address is empty", async () => { - const result = await detailsSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "address can not be empty") - }) - - it("should error on an array", async () => { - req.params.address = [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - - const result = await detailsSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "address can not be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid address", async () => { - req.params.address = `02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - const result = await detailsSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should detect a network mismatch", async () => { - req.params.address = `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - - const result = await detailsSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.params.address = `qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await detailsSingle(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should default to page 0", async () => { - req.params.address = `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert current page defaults to 0 - assert.equal(result.currentPage, 0) - }) - - it("should process the requested page", async () => { - req.params.address = `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7` - req.query.page = 5 - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsSingle(req, res) - - // Assert current page is same as requested - assert.equal(result.currentPage, 5) - }) - - it("should calculate the total number of pages", async () => { - req.params.address = `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsSingle(req, res) - - assert.equal(result.pagesTotal, 1) - }) - - it("should get details for a single address", async () => { - req.params.address = `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockAddressDetails) - } - - // Call the details API. - const result = await detailsSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert that required fields exist in the returned object. - assert.hasAllKeys(result, [ - "balance", - "balanceSat", - "totalReceived", - "totalReceivedSat", - "totalSent", - "totalSentSat", - "unconfirmedBalance", - "unconfirmedBalanceSat", - "unconfirmedTxApperances", - "txApperances", - "transactions", - "legacyAddress", - "cashAddress", - "currentPage", - "pagesTotal" - ]) - }) - }) - - describe("#AddressUtxoBulk", () => { - // utxo route handler. - const utxoBulk = addressRoute.testableComponents.utxoBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await utxoBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single address", async () => { - req.body = { - address: `qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - } - - const result = await utxoBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid address", async () => { - req.body = { - addresses: [`02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - } - - const result = await utxoBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should detect a network mismatch", async () => { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - const result = await utxoBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.body = { - addresses: [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - } - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api" - - const result = await utxoBulk(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get utxos for a single address", async () => { - req.body = { - addresses: [`bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockUtxoDetails) - } - - // Call the details API. - const result = await utxoBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result, "result should be an array") - - // Each element should have these primary properties. - assert.hasAllKeys(result[0], [ - "utxos", - "legacyAddress", - "cashAddress", - "scriptPubKey" - ]) - - // Validate the UTXO data structure. - assert.hasAnyKeys(result[0].utxos[0], [ - "txid", - "vout", - "amount", - "satoshis", - "height", - "confirmations" - ]) - }) - - it("should get utxos for mulitple addresses", async () => { - req.body = { - addresses: [ - `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7`, - `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7` - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .times(2) - .reply(200, mockData.mockUtxoDetails) - } - - // Call the details API. - const result = await utxoBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.equal(result.length, 2, "2 outputs for 2 inputs") - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.addresses = testArray - - const result = await utxoBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - }) - - describe("#AddressUtxoSingle", () => { - // details route handler. - const utxoSingle = addressRoute.testableComponents.utxoSingle - - it("should throw 400 if address is empty", async () => { - const result = await utxoSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "address can not be empty") - }) - - it("should error on an array", async () => { - req.params.address = [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - - const result = await utxoSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "address can not be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid address", async () => { - req.params.address = `02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - const result = await utxoSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should detect a network mismatch", async () => { - req.params.address = `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - - const result = await utxoSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.params.address = `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7` - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await utxoSingle(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get details for a single address", async () => { - req.params.address = `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockUtxoDetails) - } - - // Call the details API. - const result = await utxoSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Each element should have these primary properties. - assert.hasAllKeys(result, [ - "utxos", - "legacyAddress", - "cashAddress", - "scriptPubKey" - ]) - - // Validate the UTXO data structure. - assert.hasAnyKeys(result.utxos[0], [ - "txid", - "vout", - "amount", - "satoshis", - "height", - "confirmations" - ]) - }) - }) - - describe("#AddressUnconfirmedBulk", () => { - // unconfirmed route handler. - const unconfirmedBulk = addressRoute.testableComponents.unconfirmedBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await unconfirmedBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single address", async () => { - req.body = { - address: `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7` - } - - const result = await unconfirmedBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.addresses = testArray - - const result = await unconfirmedBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should throw an error for an invalid address", async () => { - req.body = { - addresses: [`02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - } - - const result = await unconfirmedBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should detect a network mismatch", async () => { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - const result = await unconfirmedBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.body = { - addresses: [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - } - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api" - - const result = await unconfirmedBulk(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get unconfirmed data for a single address", async () => { - req.body = { - addresses: [`bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockUtxoDetails) - } - - // Call the details API. - const result = await unconfirmedBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - //console.log(`result[0].utxos: ${util.inspect(result[0].utxos)}`) - - assert.isArray(result, "result should be an array") - - // Dev note: Unconfirmed TXs are hard to test in an integration test because - // the nature of an unconfirmed transation is transient. It quickly becomes - // confirmed and thus should not show up. - }) - - it("should get unconfirmed data for an array of addresses", async () => { - req.body = { - addresses: [ - `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7`, - `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7` - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockUtxoDetails) - - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockUtxoDetails) - } - - // Call the details API. - const result = await unconfirmedBulk(req, res) - - assert.isArray(result) - }) - }) - - describe("#AddressUnconfirmedSingle", () => { - // details route handler. - const unconfirmedSingle = addressRoute.testableComponents.unconfirmedSingle - - it("should throw 400 if address is empty", async () => { - const result = await unconfirmedSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "address can not be empty") - }) - - it("should error on an array", async () => { - req.params.address = [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - - const result = await unconfirmedSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "address can not be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid address", async () => { - req.params.address = `02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - const result = await unconfirmedSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should detect a network mismatch", async () => { - req.params.address = `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - - const result = await unconfirmedSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.params.address = `qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await unconfirmedSingle(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get details for a single address", async () => { - req.params.address = `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockUtxoDetails) - } - - // Call the details API. - const result = await unconfirmedSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Each element should have these primary properties. - assert.hasAllKeys(result, [ - "utxos", - "legacyAddress", - "cashAddress", - "scriptPubKey" - ]) - - assert.isArray(result.utxos) - }) - }) - - describe("#AddressTransactionsBulk", () => { - // unconfirmed route handler. - const transactionsBulk = addressRoute.testableComponents.transactionsBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await transactionsBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single address", async () => { - req.body = { - address: `qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - } - - const result = await transactionsBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.addresses = testArray - - const result = await transactionsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should throw an error for an invalid address", async () => { - req.body = { - addresses: [`02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - } - - const result = await transactionsBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should detect a network mismatch", async () => { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - const result = await transactionsBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.body = { - addresses: [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - } - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api" - - const result = await transactionsBulk(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should default to page 0", async () => { - req.body = { - addresses: [`bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockTransactions) - } - - // Call the endpoint - const result = await transactionsBulk(req, res) - - // Assert current page defaults to 0 - assert.equal(result[0].currentPage, 0) - }) - - it("should process the requested page", async () => { - req.body = { - addresses: [`bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7`], - page: 5 - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockTransactions) - } - - // Call the endpoint - const result = await transactionsBulk(req, res) - - // Assert current page is same as requested - assert.equal(result[0].currentPage, 5) - }) - - it("should get transactions for a single address", async () => { - req.body = { - addresses: [`bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockTransactions) - } - - // Call the details API. - const result = await transactionsBulk(req, res) - - assert.isArray(result, "result should be an array") - - assert.exists(result[0].pagesTotal) - assert.exists(result[0].currentPage) - assert.exists(result[0].txs) - assert.isArray(result[0].txs) - assert.exists(result[0].legacyAddress) - assert.exists(result[0].cashAddress) - }) - - it("should get transactions for an array of addresses", async () => { - req.body = { - addresses: [ - `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7`, - `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7` - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .times(2) - .reply(200, mockData.mockTransactions) - } - - // Call the details API. - const result = await transactionsBulk(req, res) - - assert.isArray(result, "result should be an array") - - assert.equal(result.length, 2, "Array should have 2 elements") - }) - }) - - describe("#AddressTransactionsSingle", () => { - // details route handler. - const transactionsSingle = - addressRoute.testableComponents.transactionsSingle - - it("should throw 400 if address is empty", async () => { - const result = await transactionsSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "address can not be empty") - }) - - it("should error on an array", async () => { - req.params.address = [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - - const result = await transactionsSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "address can not be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid address", async () => { - req.params.address = `02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - const result = await transactionsSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should detect a network mismatch", async () => { - req.params.address = `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - - const result = await transactionsSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.params.address = `qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await transactionsSingle(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should default to page 0", async () => { - req.params.address = `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockTransactions) - } - - // Call the endpoint - const result = await transactionsSingle(req, res) - - // Assert current page defaults to 0 - assert.equal(result.currentPage, 0) - }) - - it("should process the requested page", async () => { - req.params.address = `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7` - req.query.page = 5 - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockTransactions) - } - - // Call the endpoint - const result = await transactionsSingle(req, res) - - // Assert current page is same as requested - assert.equal(result.currentPage, 5) - }) - - it("should get details for a single address", async () => { - req.params.address = `bitcoincash:qp3sn6vlwz28ntmf3wmyra7jqttfx7z6zgtkygjhc7` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockTransactions) - } - - // Call the details API. - const result = await transactionsSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert that required fields exist in the returned object. - assert.exists(result.pagesTotal) - assert.exists(result.currentPage) - assert.exists(result.txs) - assert.isArray(result.txs) - assert.exists(result.legacyAddress) - assert.exists(result.cashAddress) - }) - }) - - describe("#AddressFromXPubSingle", () => { - // details route handler. - const fromXPubSingle = addressRoute.testableComponents.fromXPubSingle - - it("should throw 400 if xpub is empty", async () => { - const result = await fromXPubSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "xpub can not be empty") - }) - - it("should error on an array", async () => { - req.params.xpub = [ - `tpubDHTK2jqg73w3GwoiHfAMbMYML1HN8FhrUxD9rFgbSgHXdwwrY6pAFqKDfUHhqw7vreaZty5hPGjb1S7ZPQeMmu6TFHAKfY9tJpYbvaGjPRM` - ] - - const result = await fromXPubSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "xpub can not be an array", - "Proper error message" - ) - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.params.xpub = `tpubDHTK2jqg73w3GwoiHfAMbMYML1HN8FhrUxD9rFgbSgHXdwwrY6pAFqKDfUHhqw7vreaZty5hPGjb1S7ZPQeMmu6TFHAKfY9tJpYbvaGjPRM` - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await fromXPubSingle(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should create an address from xpub", async () => { - req.params.xpub = `tpubDHTK2jqg73w3GwoiHfAMbMYML1HN8FhrUxD9rFgbSgHXdwwrY6pAFqKDfUHhqw7vreaZty5hPGjb1S7ZPQeMmu6TFHAKfY9tJpYbvaGjPRM` - - // Mock the Insight URL for unit tests. - // TODO add unit test - // if (process.env.TEST === "unit") { - // nock(`${process.env.BITCOINCOM_BASEURL}`) - // .get( - // `/txs/?address=bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4&pageNum=0` - // ) - // .reply(200, mockData.mockTransactions) - // } - - // Call the details API. - const result = await fromXPubSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert that required fields exist in the returned object. - assert.exists(result.legacyAddress) - assert.exists(result.cashAddress) - }) - }) -}) diff --git a/test/v3/transaction.js b/test/v3/transaction.js deleted file mode 100644 index 2450134..0000000 --- a/test/v3/transaction.js +++ /dev/null @@ -1,333 +0,0 @@ -/* - TESTS FOR THE TRANSACTION.TS LIBRARY - - This test file uses the environment variable TEST to switch between unit - and integration tests. By default, TEST is set to 'unit'. Set this variable - to 'integration' to run the tests against BCH mainnet. - - TODO: - -See "should throw an error for an invalid txid" for detailsSingle: - --The error handler should be refactored to return an intelligent error message, - instead of the 503 error it is returning now. -*/ - -"use strict" - -const chai = require("chai") -const assert = chai.assert -const transactionRoute = require("../../src/routes/v3/insight/transaction") -const nock = require("nock") // HTTP mocking - -let originalEnvVars // Used during transition from integration to unit tests. - -// Mocking data. -const { mockReq, mockRes } = require("./mocks/express-mocks") -const mockData = require("./mocks/transaction-mocks") - -// Used for debugging. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -describe("#Transactions", () => { - let req, res - - before(() => { - // Save existing environment variables. - originalEnvVars = { - BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL, - RPC_BASEURL: process.env.RPC_BASEURL, - RPC_USERNAME: process.env.RPC_USERNAME, - RPC_PASSWORD: process.env.RPC_PASSWORD - } - - // Set default environment variables for unit tests. - if (!process.env.TEST) process.env.TEST = "unit" - if (process.env.TEST === "unit") { - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - process.env.RPC_BASEURL = "http://fakeurl/api" - process.env.RPC_USERNAME = "fakeusername" - process.env.RPC_PASSWORD = "fakepassword" - } - }) - - // Setup the mocks before each test. - beforeEach(() => { - // Mock the req and res objects used by Express routes. - req = mockReq - res = mockRes - - // Explicitly reset the parmas and body. - req.params = {} - req.body = {} - req.query = {} - - // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() - }) - - afterEach(() => { - // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() - }) - - after(() => { - // Restore any pre-existing environment variables. - process.env.BITCOINCOM_BASEURL = originalEnvVars.BITCOINCOM_BASEURL - process.env.RPC_BASEURL = originalEnvVars.RPC_BASEURL - process.env.RPC_USERNAME = originalEnvVars.RPC_USERNAME - process.env.RPC_PASSWORD = originalEnvVars.RPC_PASSWORD - }) - - describe("#root", async () => { - // root route handler. - const root = transactionRoute.testableComponents.root - - it("should respond to GET for base route", async () => { - const result = root(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(result.status, "transaction", "Returns static string") - }) - }) - - describe("#detailsBulk", async () => { - const detailsBulk = transactionRoute.testableComponents.detailsBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await detailsBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "txids needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single txid", async () => { - req.body = { - txids: `6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40` - } - - const result = await detailsBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "txids needs to be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid txid", async () => { - const fakeTXID = `02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/tx/${fakeTXID}`) - .reply(400, { - result: { error: "parameter 1 must be hexadecimal string" } - }) - } - - req.body = { - txids: [fakeTXID] - } - - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - }) - - it("should process a single txid", async () => { - const txid = `2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/tx/${txid}`) - .reply(200, mockData.mockDetails) - } - - req.body = { - txids: [txid] - } - - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAnyKeys(result[0], [ - "txid", - "version", - "locktime", - "vin", - "vout", - "blockhash", - "blockheight", - "confirmations", - "time", - "blocktime", - "valueOut", - "size", - "valueIn", - "fees" - ]) - }) - - it("should process a multiple txids", async () => { - const txid1 = `2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266` - const txid2 = `2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/tx/${txid1}`) - .reply(200, mockData.mockDetails) - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/tx/${txid2}`) - .reply(200, mockData.mockDetails) - } - - req.body = { - txids: [txid1, txid2] - } - - const result = await detailsBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAnyKeys(result[0], [ - "txid", - "version", - "locktime", - "vin", - "vout", - "blockhash", - "blockheight", - "confirmations", - "time", - "blocktime", - "valueOut", - "size", - "valueIn", - "fees" - ]) - }) - }) - - describe("#detailsSingle", () => { - // details route handler. - const detailsSingle = transactionRoute.testableComponents.detailsSingle - - it("should throw 400 if txid is empty", async () => { - const result = await detailsSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "txid can not be empty") - }) - - it("should error on an array", async () => { - req.params.txid = [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - - const result = await detailsSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "txid can not be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid txid", async () => { - if (process.env.TEST !== "unit") { - req.params.txid = `02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - const result = await detailsSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // The error handling code should probably be updated to respond with a better - // error message. - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "parameter 1 must be hexadecimal string", - "Proper error message" - ) - } - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.params.txid = `6f235bd3a689f03c11969cd649ccad592462ca958bc519a30194e7a67b349a40` - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await detailsSingle(req, res) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.isAbove( - res.statusCode, - 499, - "HTTP status code 500 or greater expected." - ) - //assert.include(result.error,"Network error: Could not communicate with full node","Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get details for a single txid", async () => { - const txid = `2c7ae9f865f7ce0c33c189b2f83414176903ce4b06ed9f8b7bcf55efbd4a7266` - req.params.txid = txid - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCOINCOM_BASEURL}`) - .get(`/tx/${txid}`) - .reply(200, mockData.mockDetails) - } - - // Call the details API. - const result = await detailsSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - // Assert that required fields exist in the returned object. - assert.hasAllKeys(result, [ - "txid", - "version", - "locktime", - "vin", - "vout", - "blockhash", - "blockheight", - "confirmations", - "time", - "blocktime", - "valueOut", - "size", - "valueIn", - "fees" - ]) - assert.isArray(result.vin) - assert.isArray(result.vout) - }) - }) -}) From b0c5b4c6817952bdcead29275baa96dd47bb0782 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Fri, 6 Dec 2019 16:17:43 -0800 Subject: [PATCH 30/49] Removed bitcore as a downstream service --- src/app.js | 2 - src/routes/v3/bitcore.js | 395 ---------------------- test/v3/integration/bitcore.js | 599 --------------------------------- 3 files changed, 996 deletions(-) delete mode 100644 src/routes/v3/bitcore.js delete mode 100644 test/v3/integration/bitcore.js diff --git a/src/app.js b/src/app.js index 6010573..54661d7 100644 --- a/src/app.js +++ b/src/app.js @@ -30,7 +30,6 @@ const rawtransactionsV3 = require("./routes/v3/full-node/rawtransactions") const utilV3 = require("./routes/v3/util") const slpV3 = require("./routes/v3/slp") const xpubV3 = require("./routes/v3/xpub") -const bitcoreV3 = require("./routes/v3/bitcore") const blockbookV3 = require("./routes/v3/blockbook") require("dotenv").config() @@ -92,7 +91,6 @@ app.use(`/${v3prefix}/` + `rawtransactions`, rawtransactionsV3.router) app.use(`/${v3prefix}/` + `util`, utilV3.router) app.use(`/${v3prefix}/` + `slp`, slpV3.router) app.use(`/${v3prefix}/` + `xpub`, xpubV3.router) -app.use(`/${v3prefix}/` + `bitcore`, bitcoreV3.router) app.use(`/${v3prefix}/` + `blockbook`, blockbookV3.router) // catch 404 and forward to error handler diff --git a/src/routes/v3/bitcore.js b/src/routes/v3/bitcore.js deleted file mode 100644 index 6bccd26..0000000 --- a/src/routes/v3/bitcore.js +++ /dev/null @@ -1,395 +0,0 @@ -/* - Bitcore Node API route -*/ - -"use strict" - -const express = require("express") -const axios = require("axios") -const routeUtils = require("./route-utils") -const wlogger = require("../../util/winston-logging") - -const router = express.Router() - -// Used for processing error messages before sending them to the user. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -const BCHJS = require("@chris.troutner/bch-js") -const bchjs = new BCHJS() - -//const BITCORE_URL = process.env.BITCORE_URL - -// Connect the route endpoints to their handler functions. -router.get("/", root) -router.get("/balance/:address", balanceSingle) -router.post("/balance", balanceBulk) -router.get("/utxos/:address", utxosSingle) -router.post("/utxos", utxosBulk) - -// Root API endpoint. Simply acknowledges that it exists. -function root(req, res, next) { - return res.json({ status: "address" }) -} - -// Query the Bitcore Node API for a balance on a single BCH address. -// Returns a Promise. -async function balanceFromBitcore(thisAddress) { - try { - //console.log(`BITCORE_URL: ${BITCORE_URL}`) - - // Convert the address to a cashaddr without a prefix. - const addr = bchjs.Address.toCashAddress(thisAddress, false) - - // Determine if we are working with the testnet or mainnet networks. - let network = "mainnet" - if (process.env.NETWORK === "testnet") network = "testnet" - - const path = `${process.env.BITCORE_URL}api/BCH/${network}/address/${addr}/balance` - - //console.log(`path: ${path}`) - - // Query the Bitcore Node API. - const axiosResponse = await axios.get(path) - const retData = axiosResponse.data - //console.log(`retData: ${util.inspect(retData)}`) - - return retData - } catch (err) { - // Dev Note: Do not log error messages here. Throw them instead and let the - // parent function handle it. - throw err - } -} - -/** - * @api {get} /bitcore/balance/:address Get BCH balance for an address - * @apiName GetBalance - * @apiGroup Bitcore - * - * @apiParam {String} address Bitcoin Cash address. - * - * @apiSuccess {String} firstname Firstname of the User. - * @apiSuccess {String} lastname Lastname of the User. - */ -// GET handler for single balance -async function balanceSingle(req, res, next) { - try { - const address = req.params.address - - if (!address || address === "") { - res.status(400) - return res.json({ error: "address can not be empty" }) - } - - // Reject if address is an array. - if (Array.isArray(address)) { - res.status(400) - return res.json({ - error: "address can not be an array. Use POST for bulk upload." - }) - } - - wlogger.debug( - `Executing bitcore/balanceSingle with this address: `, - address - ) - - // Ensure the input is a valid BCH address. - try { - const legacyAddr = bchjs.Address.toLegacyAddress(address) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${address}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(address) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - - // Query the Bitcore Node API. - const retData = await balanceFromBitcore(address) - - // Return the retrieved address information. - res.status(200) - return res.json(retData) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - wlogger.error(`Error in bitcore.js/balanceSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// POST handler for bulk queries on address details -// curl -d '{"addresses": ["bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", "bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"]}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v2/address/details -// curl -d '{"addresses": ["bchtest:qzjtnzcvzxx7s0na88yrg3zl28wwvfp97538sgrrmr", "bchtest:qp6hgvevf4gzz6l7pgcte3gaaud9km0l459fa23dul"], "from": 1, "to": 5}' -H "Content-Type: application/json" https://mainnet.bchjs.cash/v2/address/details -async function balanceBulk(req, res, next) { - try { - let addresses = req.body.addresses - const currentPage = req.body.page ? parseInt(req.body.page, 10) : 0 - - // Reject if addresses is not an array. - if (!Array.isArray(addresses)) { - res.status(400) - return res.json({ - error: "addresses needs to be an array. Use GET for single address." - }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, addresses)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - wlogger.debug( - `Executing bitcore.js/balanceBulk with these addresses: `, - addresses - ) - - // Validate each element in the address array. - for (let i = 0; i < addresses.length; i++) { - const thisAddress = addresses[i] - - // Ensure the input is a valid BCH address. - try { - bchjs.Address.toLegacyAddress(thisAddress) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${thisAddress}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(thisAddress) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network for address ${thisAddress}. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - } - - // Loops through each address and creates an array of Promises, querying - // Insight API in parallel. - addresses = addresses.map(async (address, index) => - balanceFromBitcore(address) - ) - - // Wait for all parallel Insight requests to return. - const result = await axios.all(addresses) - - // Return the array of retrieved address information. - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - wlogger.error(`Error in bitcore.js/balanceBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// Query the Bitcore Node API for utxos associated with a BCH address. -// Returns a Promise. -async function utxosFromBitcore(thisAddress) { - try { - //console.log(`BITCORE_URL: ${BITCORE_URL}`) - - // Convert the address to a cashaddr without a prefix. - const addr = bchjs.Address.toCashAddress(thisAddress, false) - - // Determine if we are working with the testnet or mainnet networks. - let network = "mainnet" - if (process.env.NETWORK === "testnet") network = "testnet" - - const path = `${process.env.BITCORE_URL}api/BCH/${network}/address/${addr}/?unspent=true` - - // Query the Bitcore Node API. - const axiosResponse = await axios.get(path) - const retData = axiosResponse.data - //console.log(`retData: ${util.inspect(retData)}`) - - return retData - } catch (err) { - // Dev Note: Do not log error messages here. Throw them instead and let the - // parent function handle it. - throw err - } -} - -// GET handler for single balance -async function utxosSingle(req, res, next) { - try { - const address = req.params.address - - if (!address || address === "") { - res.status(400) - return res.json({ error: "address can not be empty" }) - } - - // Reject if address is an array. - if (Array.isArray(address)) { - res.status(400) - return res.json({ - error: "address can not be an array. Use POST for bulk upload." - }) - } - - wlogger.debug(`Executing bitcore/utxoSingle with this address: `, address) - - // Ensure the input is a valid BCH address. - try { - const legacyAddr = bchjs.Address.toLegacyAddress(address) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${address}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(address) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - - // Query the Bitcore Node API. - const retData = await utxosFromBitcore(address) - - // Return the retrieved address information. - res.status(200) - return res.json(retData) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - // Write out error to error log. - wlogger.error(`Error in bitcore.js/utxosSingle().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -// POST handler for bulk queries on address utxos -async function utxosBulk(req, res, next) { - try { - let addresses = req.body.addresses - const currentPage = req.body.page ? parseInt(req.body.page, 10) : 0 - - // Reject if addresses is not an array. - if (!Array.isArray(addresses)) { - res.status(400) - return res.json({ - error: "addresses needs to be an array. Use GET for single address." - }) - } - - // Enforce array size rate limits - if (!routeUtils.validateArraySize(req, addresses)) { - res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 - return res.json({ - error: `Array too large.` - }) - } - - wlogger.debug( - `Executing bitcore.js/utxosBulk with these addresses: `, - addresses - ) - - // Validate each element in the address array. - for (let i = 0; i < addresses.length; i++) { - const thisAddress = addresses[i] - - // Ensure the input is a valid BCH address. - try { - bchjs.Address.toLegacyAddress(thisAddress) - } catch (err) { - res.status(400) - return res.json({ - error: `Invalid BCH address. Double check your address is valid: ${thisAddress}` - }) - } - - // Prevent a common user error. Ensure they are using the correct network address. - const networkIsValid = routeUtils.validateNetwork(thisAddress) - if (!networkIsValid) { - res.status(400) - return res.json({ - error: `Invalid network for address ${thisAddress}. Trying to use a testnet address on mainnet, or vice versa.` - }) - } - } - - // Loops through each address and creates an array of Promises, querying - // Insight API in parallel. - addresses = addresses.map(async (address, index) => - utxosFromBitcore(address) - ) - - // Wait for all parallel Insight requests to return. - const result = await axios.all(addresses) - - // Return the array of retrieved address information. - res.status(200) - return res.json(result) - } catch (err) { - // Attempt to decode the error message. - const { msg, status } = routeUtils.decodeError(err) - if (msg) { - res.status(status) - return res.json({ error: msg }) - } - - wlogger.error(`Error in bitcore.js/utxosBulk().`, err) - - res.status(500) - return res.json({ error: util.inspect(err) }) - } -} - -module.exports = { - router, - testableComponents: { - root, - balanceSingle, - balanceBulk, - utxosSingle, - utxosBulk - } -} diff --git a/test/v3/integration/bitcore.js b/test/v3/integration/bitcore.js deleted file mode 100644 index fd6cf67..0000000 --- a/test/v3/integration/bitcore.js +++ /dev/null @@ -1,599 +0,0 @@ -/* - TESTS FOR THE BITCORE.JS LIBRARY - - This test file uses the environment variable TEST to switch between unit - and integration tests. By default, TEST is set to 'unit'. Set this variable - to 'integration' to run the tests against BCH mainnet. - - To-Do: -*/ - -"use strict" - -const chai = require("chai") -const assert = chai.assert -const bitcoreRoute = require("../../src/routes/v3/bitcore") -const nock = require("nock") // HTTP mocking - -let originalUrl // Used during transition from integration to unit tests. - -// Mocking data. -const { mockReq, mockRes } = require("./mocks/express-mocks") -const mockData = require("./mocks/bitcore-mock") - -// Used for debugging. -const util = require("util") -util.inspect.defaultOptions = { depth: 1 } - -describe("#Bitcore Router", () => { - let req, res, mockServerUrl - - before(() => { - originalUrl = process.env.BITCORE_URL - - // Set default environment variables for unit tests. - if (!process.env.TEST) process.env.TEST = "unit" - if (process.env.TEST === "unit") { - process.env.BITCORE_URL = "http://fakeurl/api/" - mockServerUrl = `http://fakeurl` - } - // console.log(`Testing type is: ${process.env.TEST}`) - }) - - // Setup the mocks before each test. - beforeEach(() => { - // Mock the req and res objects used by Express routes. - req = mockReq - res = mockRes - - // Explicitly reset the parmas and body. - req.params = {} - req.body = {} - req.query = {} - - // Activate nock if it's inactive. - if (!nock.isActive()) nock.activate() - }) - - afterEach(() => { - // Clean up HTTP mocks. - nock.cleanAll() // clear interceptor list. - nock.restore() - }) - - after(() => { - process.env.BITCORE_URL = originalUrl - }) - - describe("#root", () => { - // root route handler. - const root = bitcoreRoute.testableComponents.root - - it("should respond to GET for base route", async () => { - const result = root(req, res) - - assert.equal(result.status, "address", "Returns static string") - }) - }) - - describe("#Balance Single", () => { - // details route handler. - const balanceSingle = bitcoreRoute.testableComponents.balanceSingle - - it("should throw 400 if address is empty", async () => { - const result = await balanceSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "address can not be empty") - }) - - it("should error on an array", async () => { - req.params.address = [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - - const result = await balanceSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "address can not be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid address", async () => { - req.params.address = `02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - const result = await balanceSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should detect a network mismatch", async () => { - req.params.address = `bitcoincash:qqqvv56zepke5k0xeaehlmjtmkv9ly2uzgkxpajdx3` - - const result = await balanceSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCORE_URL - - try { - req.params.address = `qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - // Switch the Insight URL to something that will error out. - process.env.BITCORE_URL = "http://fakeurl/api/" - - const result = await balanceSingle(req, res) - - // Restore the saved URL. - process.env.BITCORE_URL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCORE_URL = savedUrl - } - }) - - it("should get balance for a single address", async () => { - req.params.address = `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCORE_URL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockBalance) - } - - // Call the details API. - const result = await balanceSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["confirmed", "unconfirmed", "balance"]) - assert.isNumber(result.confirmed) - assert.isNumber(result.unconfirmed) - assert.isNumber(result.balance) - }) - }) - - describe("#Balance Bulk", () => { - // details route handler. - const balanceBulk = bitcoreRoute.testableComponents.balanceBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await balanceBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single address", async () => { - req.body = { - address: `qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - } - - const result = await balanceBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid address", async () => { - req.body = { - addresses: [`02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - } - - const result = await balanceBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.addresses = testArray - - const result = await balanceBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should detect a network mismatch", async () => { - req.body = { - addresses: [`bitcoincash:qqqvv56zepke5k0xeaehlmjtmkv9ly2uzgkxpajdx3`] - } - - const result = await balanceBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await balanceBulk(req, res) - //console.log(`network issue result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.isAbove(res.statusCode, 499, "HTTP status code 500 expected.") - //assert.include(result.error, "ENOTFOUND", "Error message expected") - assert.include( - result.error, - "Network error: Could not communicate", - "Error message expected" - ) - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get details for a single address", async () => { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(mockServerUrl) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockBalance) - } - - // Call the details API. - const result = await balanceBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAllKeys(result[0], ["confirmed", "unconfirmed", "balance"]) - assert.isNumber(result[0].confirmed) - assert.isNumber(result[0].unconfirmed) - assert.isNumber(result[0].balance) - }) - - it("should get details for multiple addresses", async () => { - req.body = { - addresses: [ - `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`, - `bchtest:qzknfggae0av6yvxk77gmyq7syc67yux6sk80haqyr` - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(mockServerUrl) - .get(uri => uri.includes("/")) - .times(2) - .reply(200, mockData.mockBalance) - } - - // Call the details API. - const result = await balanceBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.equal(result.length, 2, "2 outputs for 2 inputs") - }) - }) - - describe("#UTXOs Single", () => { - // details route handler. - const utxosSingle = bitcoreRoute.testableComponents.utxosSingle - - it("should throw 400 if address is empty", async () => { - const result = await utxosSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "address can not be empty") - }) - - it("should error on an array", async () => { - req.params.address = [`qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - - const result = await utxosSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "address can not be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid address", async () => { - req.params.address = `02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - const result = await utxosSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should detect a network mismatch", async () => { - req.params.address = `bitcoincash:qqqvv56zepke5k0xeaehlmjtmkv9ly2uzgkxpajdx3` - - const result = await utxosSingle(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCORE_URL - - try { - req.params.address = `qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - - // Switch the Insight URL to something that will error out. - process.env.BITCORE_URL = "http://fakeurl/api/" - - const result = await utxosSingle(req, res) - - // Restore the saved URL. - process.env.BITCORE_URL = savedUrl - - assert.equal(res.statusCode, 500, "HTTP status code 500 expected.") - assert.include(result.error, "ENOTFOUND", "Error message expected") - } catch (err) { - // Restore the saved URL. - process.env.BITCORE_URL = savedUrl - } - }) - - it("should get utxos for a single address", async () => { - req.params.address = `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4` - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(`${process.env.BITCORE_URL}`) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockUtxos) - } - - // Call the details API. - const result = await utxosSingle(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.hasAnyKeys(result[0], [ - "_id", - "chain", - "network", - "coinbase", - "mintIndex", - "spentTxid", - "mintTxid", - "mintHeight", - "spentHeight", - "address", - "script", - "value", - "confirmations" - ]) - }) - }) - - describe("#UTXO Bulk", () => { - // details route handler. - const utxosBulk = bitcoreRoute.testableComponents.utxosBulk - - it("should throw an error for an empty body", async () => { - req.body = {} - - const result = await utxosBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should error on non-array single address", async () => { - req.body = { - address: `qzs02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c` - } - - const result = await utxosBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "addresses needs to be an array", - "Proper error message" - ) - }) - - it("should throw an error for an invalid address", async () => { - req.body = { - addresses: [`02v05l7qs5s24srqju498qu55dwuj0cx5ehjm2c`] - } - - const result = await utxosBulk(req, res) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include( - result.error, - "Invalid BCH address", - "Proper error message" - ) - }) - - it("should throw 400 error if addresses array is too large", async () => { - const testArray = [] - for (var i = 0; i < 25; i++) testArray.push("") - - req.body.addresses = testArray - - const result = await utxosBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.hasAllKeys(result, ["error"]) - assert.include(result.error, "Array too large") - }) - - it("should detect a network mismatch", async () => { - req.body = { - addresses: [`bitcoincash:qqqvv56zepke5k0xeaehlmjtmkv9ly2uzgkxpajdx3`] - } - - const result = await utxosBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.equal(res.statusCode, 400, "HTTP status code 400 expected.") - assert.include(result.error, "Invalid network", "Proper error message") - }) - - it("should throw 500 when network issues", async () => { - const savedUrl = process.env.BITCOINCOM_BASEURL - - try { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - // Switch the Insight URL to something that will error out. - process.env.BITCOINCOM_BASEURL = "http://fakeurl/api/" - - const result = await utxosBulk(req, res) - //console.log(`network issue result: ${util.inspect(result)}`) - - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - - assert.isAbove(res.statusCode, 499, "HTTP status code 500 expected.") - //assert.include(result.error, "ENOTFOUND", "Error message expected") - assert.include( - result.error, - "Network error: Could not communicate", - "Error message expected" - ) - } catch (err) { - // Restore the saved URL. - process.env.BITCOINCOM_BASEURL = savedUrl - } - }) - - it("should get details for a single address", async () => { - req.body = { - addresses: [`bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(mockServerUrl) - .get(uri => uri.includes("/")) - .reply(200, mockData.mockUtxos) - } - - // Call the details API. - const result = await utxosBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.isArray(result[0]) - assert.hasAnyKeys(result[0][0], [ - "_id", - "chain", - "network", - "coinbase", - "mintIndex", - "spentTxid", - "mintTxid", - "mintHeight", - "spentHeight", - "address", - "script", - "value", - "confirmations" - ]) - }) - - it("should get details for multiple addresses", async () => { - req.body = { - addresses: [ - `bchtest:qq89kjkeqz9mngp8kl3dpmu43y2wztdjqu500gn4c4`, - `bchtest:qzknfggae0av6yvxk77gmyq7syc67yux6sk80haqyr` - ] - } - - // Mock the Insight URL for unit tests. - if (process.env.TEST === "unit") { - nock(mockServerUrl) - .get(uri => uri.includes("/")) - .times(2) - .reply(200, mockData.mockUtxos) - } - - // Call the details API. - const result = await utxosBulk(req, res) - //console.log(`result: ${util.inspect(result)}`) - - assert.isArray(result) - assert.isArray(result[0]) - assert.hasAnyKeys(result[0][0], [ - "_id", - "chain", - "network", - "coinbase", - "mintIndex", - "spentTxid", - "mintTxid", - "mintHeight", - "spentHeight", - "address", - "script", - "value", - "confirmations" - ]) - }) - }) -}) From 20b7425945d5ed76d63bf97560a452ca6a08d1d0 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 7 Dec 2019 19:47:35 -0800 Subject: [PATCH 31/49] fix(homepage): Updated the static page served by api.bchjs.cash --- src/public/index.html | 191 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 164 insertions(+), 27 deletions(-) diff --git a/src/public/index.html b/src/public/index.html index 28ed44e..be807b4 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -1,35 +1,172 @@ - + - - + + - BCH API - - + BCH API + + - + + - + +

BCH API

+

The REST API for the Bitcoin Cash blockchain.

+
- -

BCH API

-

- This web server is based on rest.bitcoin.com. - Go there to view an interactive UI of the REST API endpoints. -

- +

+ bch-api is a + REST API, allowing modern applications to interact with the Bitcoin Cash (BCH) + blockchain. New to the BCH blockchain? Start by reading more about + building full stack applications on BCH. +

+

+ A REST API can work with any programming language, but bch-api is intended + to be paired with the + npm bch-js + JavaScript library. + Here is example code + for common BCH use cases. + + slp-cli-wallet + is another npm library that provides common wallet functionality, to get + your apps up and running as quickly as possible. +

+

+ While you'll be able to run through the examples without one, you should + sign-up for a free API key + in order to unlock higher rate limits. +

- - +
+

Quick Links

+ + +
+

Credit

+

+ This web server is based on + rest.bitcoin.com. Most endpoints + are common between the two projects. You may want to plan with their + interactive UI to explore the API endpoints. +

+ + + + From 708c1e1935993c05600e14a5eb831aa00e999c21 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 7 Dec 2019 19:49:51 -0800 Subject: [PATCH 32/49] Added a few more links --- src/public/index.html | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/public/index.html b/src/public/index.html index be807b4..29c3616 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -71,6 +71,7 @@
+
  • bch-api documentation @@ -78,6 +79,13 @@

  • +
  • + + testnet REST API + +
  • +
    +
  • bch-api GitHub repository @@ -118,6 +126,13 @@

  • + +
  • + + Testnet Faucet + +
  • +
    From d4c356f2c0c0cc5d9c9cd69d083323120c3042e8 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 7 Dec 2019 19:54:32 -0800 Subject: [PATCH 33/49] Adding a little more space on the homepage --- src/public/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/public/index.html b/src/public/index.html index 29c3616..a42f0b9 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -29,7 +29,7 @@ target="_blank" >building full stack applications on BCH. -

    +


    A REST API can work with any programming language, but bch-api is intended to be paired with the @@ -50,7 +50,7 @@ > is another npm library that provides common wallet functionality, to get your apps up and running as quickly as possible. -

    +


    While you'll be able to run through the examples without one, you should Date: Sun, 8 Dec 2019 08:07:07 -0800 Subject: [PATCH 34/49] Adding link to telegram channel --- src/public/index.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/public/index.html b/src/public/index.html index a42f0b9..563dbd3 100644 --- a/src/public/index.html +++ b/src/public/index.html @@ -92,6 +92,13 @@
    + +

  • + + Community Telegram Channel + +
  • +
    From 592510559b8c6a1bd9cb72d5061c6ede898c797b Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 8 Dec 2019 08:28:00 -0800 Subject: [PATCH 35/49] fix(READMEs): updating readmes --- README.md | 32 ++++++++++++++++----------- docker/mainnet/start-local-mainnet.sh | 14 ++++-------- docker/testnet/start-local-testnet.sh | 14 ++++-------- start-dev-example.sh | 12 +++------- 4 files changed, 30 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 9eaf9ee..02d68a9 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,14 @@ the [rest.bitcoin.com](https://github.com/Bitcoin-com/rest.bitcoin.com) reposito The purpose of this code is to create a REST API server that provides a common interface for working with a Bitcoin Cash full node and various indexers. -This repository is intended to be paired with my alternative implementation -of [BITBOX SDK](https://github.com/Bitcoin-com/bitbox-sdk): -[bch-js](https://github.com/christroutner/bch-js), and is part of the product -offering at [bchjs.cash](https://bchjs.cash). +This repository is intended to be paired with [bch-js](https://github.com/christroutner/bch-js), +an npm JavaScript library, and an alternative implementation +of [BITBOX SDK](https://github.com/Bitcoin-com/bitbox-sdk). -- [API Documentation](https://bchjs.cash/bch-api/index.html) +Both bch-api and bch-js are part of the +[full stack of BCH software](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer). + +- [API Documentation](https://api.bchjs.cash/docs/) - Video: [Basic Concepts](https://www.youtube.com/watch?v=o0FfW5rZPFs) - Video: [Application Stack](https://youtu.be/8w0CpQ8oydA) - [bchjs.cash](https://bchjs.cash): Buy a turn-key REST API microserver. @@ -25,8 +27,11 @@ Have questions? Need help? Join our community support ## Features The following features set this repository apart from rest.bitcoin.com: -- Address balance and UTXO queries for [Blockbook](https://github.com/trezor/blockbook) and [Bitcore](https://github.com/bitpay/bitcore/tree/master/packages/bitcore-node) added. -- Rate limits are set to 10 RPM by default, and 60 RPM if Basic Authentiction header is used. +- Address balance and UTXO queries use the [Blockbook](https://github.com/trezor/blockbook) +indexer instead of Insight. +- Fine grain access is controlled with a JWT token using +[this back end auth server](https://github.com/Permissionless-Software-Foundation/jwt-bch-api) and [this front end](https://github.com/Permissionless-Software-Foundation/jwt-bch-frontend). +- Default rate limits are set to 3 RPM for anonymous connections, up to 100 RPM for a full-access JWT token is used. - Typescript removed and ES8 JavaScript used instead. - npm audit run on all dependencies. - [Greenkeeper](https://greenkeeper.io/) implemented for automatic dependency management @@ -34,15 +39,16 @@ and security updates. ## Live Demo You can test a live demo of the REST API by running the -[bch-js examples](https://github.com/christroutner/bch-js/tree/master/examples). -Rate limits are 10 requests per minute. This is fast enough to try out the examples +[bch-js examples](https://github.com/Permissionless-Software-Foundation/bch-js-examples). +Rate limits are 3 requests per minute, but you can increase them to 10 with a free +account at [account.bchjs.cash](https://account.bchjs.cash). +This is fast enough to try out the examples but these servers are not intended as a freemium service. You can run your own REST server by purchasing the hard drive at [bchjs.cash](https://bchjs.cash). -- Mainnet REST API server: http://decatur.hopto.org:12400/v3/ - - Check server status: http://decatur.hopto.org:12401 -- Testnet REST API server: http://decatur.hopto.org:13400/v3/ - - Check server status: http://decatur.hopto.org:13401 +- Mainnet REST API server: https://api.bchjs.cash/v3/ +- Testnet REST API server: https://tapi.bchjs.cash/v3/ +- Check server status: https://metrics.bchjs.cash ## Installation There are two installation paths, depending if you want a *development* or diff --git a/docker/mainnet/start-local-mainnet.sh b/docker/mainnet/start-local-mainnet.sh index c9a3809..6573614 100755 --- a/docker/mainnet/start-local-mainnet.sh +++ b/docker/mainnet/start-local-mainnet.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Harddrive Mainnet full node +# Full node export RPC_BASEURL=http://172.17.0.1:8332/ export RPC_USERNAME=bitcoin export RPC_PASSWORD=password @@ -8,17 +8,11 @@ export ZEROMQ_PORT=28332 export ZEROMQ_URL=172.17.0.1 export NETWORK=mainnet -# Team DO QA Testnet Insight Server -export BITCOINCOM_BASEURL=http://172.17.0.1:12100/api/ - -# Testnet SLPDB -#export SLPDB_URL=https://slpdb.bchdata.cash/ +# SLPDB +#export SLPDB_URL=https://slpdb.bitcoin.com/ export SLPDB_URL=http://172.17.0.1:12300/ -# Testnet Bitcore Node API -export BITCORE_URL=http://172.17.0.1:12200/ - -# Testnet Blockbook +# Blockbook Indexer export BLOCKBOOK_URL=https://172.17.0.1:9131/ # Allow node.js to make network calls to https using self-signed certificate. export NODE_TLS_REJECT_UNAUTHORIZED=0 diff --git a/docker/testnet/start-local-testnet.sh b/docker/testnet/start-local-testnet.sh index 71a4741..3a2e033 100755 --- a/docker/testnet/start-local-testnet.sh +++ b/docker/testnet/start-local-testnet.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Harddrive Mainnet full node +# Full node export RPC_BASEURL=http://172.17.0.1:18332/ export RPC_USERNAME=bitcoin export RPC_PASSWORD=password @@ -8,17 +8,11 @@ export ZEROMQ_PORT=38332 export ZEROMQ_URL=172.17.0.1 export NETWORK=testnet -# Team DO QA Testnet Insight Server -export BITCOINCOM_BASEURL=http://172.17.0.1:13100/api/ - -# Testnet SLPDB -#export SLPDB_URL=https://slpdb.bchdata.cash/ +# SLPDB +#export SLPDB_URL=https://tslpdb.bitcoin.com/ export SLPDB_URL=http://172.17.0.1:13300/ -# Testnet Bitcore Node API -export BITCORE_URL=http://172.17.0.1:13200/ - -# Testnet Blockbook +# Blockbook export BLOCKBOOK_URL=https://172.17.0.1:19131/ # Allow node.js to make network calls to https using self-signed certificate. export NODE_TLS_REJECT_UNAUTHORIZED=0 diff --git a/start-dev-example.sh b/start-dev-example.sh index 50934c5..4d0ae09 100755 --- a/start-dev-example.sh +++ b/start-dev-example.sh @@ -1,21 +1,15 @@ #!/bin/bash -# Harddrive Mainnet full node +# Full node export RPC_BASEURL=http://:8332/ export RPC_USERNAME= export RPC_PASSWORD= export NETWORK=mainnet -# Team DO QA Testnet Insight Server -export BITCOINCOM_BASEURL=http://:12100/api/ - -# Testnet SLPDB +# SLPDB export SLPDB_URL=http://:12300/ -# Testnet Bitcore Node API -export BITCORE_URL=http://:12200/ - -# Testnet Blockbook +# Blockbook export BLOCKBOOK_URL=https://:9131/ # Allow node.js to make network calls to https using self-signed certificate. export NODE_TLS_REJECT_UNAUTHORIZED=0 From 280c8a80724b40d5fd2140691a89a55d47c6d718 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Tue, 10 Dec 2019 06:54:24 -0800 Subject: [PATCH 36/49] fix(rate limit): Fixed free 10 RPM tier --- src/middleware/route-ratelimit.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index b67c045..8d92413 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -6,8 +6,8 @@ Current rate limiting rules in requests-per-minute: - anonymous access: 3 - - free access: 10 - - any paid tier: 100 + - free access: 10, apiLevel = 0 + - any paid tier: 100, apiLevel > 0 */ "use strict" @@ -103,9 +103,9 @@ const routeRateLimit = async function(req, res, next) { const proRateLimits = req.locals.proLimit // Pro level rate limits - if (proRateLimits) { + if (proRateLimits !== undefined) { // TODO: replace the console.logs with calls to our logging system. - //console.log(`applying pro-rate limits`) + console.log(`applying pro-rate limits`) let PRO_RPM = 10 // Default value for free tier if (req.locals.apiLevel > 0) PRO_RPM = 100 // RPM for paid tiers. @@ -130,7 +130,7 @@ const routeRateLimit = async function(req, res, next) { // Freemium level rate limits } else { // TODO: replace the console.logs with calls to our logging system. - //console.log(`applying freemium limits`) + console.log(`applying freemium limits`) // Create new RateLimit if none exists for this route if (!uniqueRateLimits[route]) { From 17a2bbe3922faebef86f308d8325f7becc55c04f Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Tue, 10 Dec 2019 06:56:04 -0800 Subject: [PATCH 37/49] Commenting out debugging console logs --- src/middleware/route-ratelimit.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index 8d92413..5b78ff9 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -105,7 +105,7 @@ const routeRateLimit = async function(req, res, next) { // Pro level rate limits if (proRateLimits !== undefined) { // TODO: replace the console.logs with calls to our logging system. - console.log(`applying pro-rate limits`) + // console.log(`applying pro-rate limits`) let PRO_RPM = 10 // Default value for free tier if (req.locals.apiLevel > 0) PRO_RPM = 100 // RPM for paid tiers. @@ -130,7 +130,7 @@ const routeRateLimit = async function(req, res, next) { // Freemium level rate limits } else { // TODO: replace the console.logs with calls to our logging system. - console.log(`applying freemium limits`) + // console.log(`applying freemium limits`) // Create new RateLimit if none exists for this route if (!uniqueRateLimits[route]) { From 7ff381c3d06cdba98fa364cc074d1a3e822622b3 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Tue, 10 Dec 2019 08:33:39 -0800 Subject: [PATCH 38/49] fix(rate limits): fixing rate limits --- src/middleware/route-ratelimit.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index 5b78ff9..172d0d1 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -102,8 +102,10 @@ const routeRateLimit = async function(req, res, next) { // This boolean value is passed from the auth.js middleware. const proRateLimits = req.locals.proLimit + // console.log(`proRateLimits: ${proRateLimits}`) + // Pro level rate limits - if (proRateLimits !== undefined) { + if (proRateLimits || proRateLimits === 0) { // TODO: replace the console.logs with calls to our logging system. // console.log(`applying pro-rate limits`) @@ -169,6 +171,9 @@ function evalUserPermissioins(req, authData) { apiLevel: authData.apiLevel } + // if apiLevel = 0 (free tier), then return the default values. + if (retObj.apiLevel === 0) return retObj + const level20Routes = ["insight", "bitcore", "blockbook"] const locals = req.locals From c1bd8fbe37e9daca101678e925d47098dd8b612f Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Tue, 10 Dec 2019 12:56:49 -0800 Subject: [PATCH 39/49] fix(rate limits): Fixed corner case with JWT token handling --- src/middleware/route-ratelimit.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index 172d0d1..7c75acf 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -93,12 +93,15 @@ const routeRateLimit = async function(req, res, next) { const route = rateLimitTier + req.method + + req.locals.apiLevel + // Generates new rate limit when user upgrades JWT token. path .split("/") .slice(0, 4) .join("/") //console.log(`route identifier: ${JSON.stringify(route, null, 2)}`) + console.log(`req.locals: ${JSON.stringify(req.locals, null, 2)}`) + // This boolean value is passed from the auth.js middleware. const proRateLimits = req.locals.proLimit @@ -112,6 +115,8 @@ const routeRateLimit = async function(req, res, next) { let PRO_RPM = 10 // Default value for free tier if (req.locals.apiLevel > 0) PRO_RPM = 100 // RPM for paid tiers. + // console.log(`PRO_RPM: ${PRO_RPM}, apiLevel: ${req.locals.apiLevel}`) + // Create new RateLimit if none exists for this route if (!uniqueRateLimits[route]) { uniqueRateLimits[route] = new RateLimit({ @@ -123,7 +128,7 @@ const routeRateLimit = async function(req, res, next) { res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 return res.json({ - error: `Too many requests. Limits are ${PRO_RPM} requests per minute.` + error: `Too many requests. Limits are ${PRO_RPM} requests per minute. Increase rate limits at https://account.bchjs.cash` }) } }) @@ -145,7 +150,7 @@ const routeRateLimit = async function(req, res, next) { res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330 return res.json({ - error: `Too many requests. Your limits are currently ${maxRequests} requests per minute.` + error: `Too many requests. Your limits are currently ${maxRequests} requests per minute. Increase rate limits at https://account.bchjs.cash` }) } }) From c982c247f71171edd30f335d05362077fc7188c7 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Tue, 10 Dec 2019 13:10:37 -0800 Subject: [PATCH 40/49] Cleaning up code comments --- src/middleware/route-ratelimit.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index 7c75acf..cdad7a6 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -8,6 +8,11 @@ - anonymous access: 3 - free access: 10, apiLevel = 0 - any paid tier: 100, apiLevel > 0 + + If a person signs up for full node access but not indexer access, then the + apiLevel will be 10. If they call an endpoint that uses an indexer, the apiLevel + will be downgraded to 0 on-the-fly. Indexer endpoints will effectively be + downgraded to the anonymous access tier. */ "use strict" @@ -72,15 +77,6 @@ const routeRateLimit = async function(req, res, next) { req.locals.proLimit = userPermissions.proLimit req.locals.apiLevel = userPermissions.apiLevel - - // const locals = req.locals - // console.log(`locals: ${JSON.stringify(locals, null, 2)}`) - // const url = req.url - // console.log(`url: ${JSON.stringify(url, null, 2)}`) - // - // // console.log(`JWT is valid. Enabling pro-tier rate limits.`) - // req.locals.proLimit = true - // req.locals.apiLevel = jwtInfo.apiLevel } } } @@ -100,7 +96,7 @@ const routeRateLimit = async function(req, res, next) { .join("/") //console.log(`route identifier: ${JSON.stringify(route, null, 2)}`) - console.log(`req.locals: ${JSON.stringify(req.locals, null, 2)}`) + // console.log(`req.locals: ${JSON.stringify(req.locals, null, 2)}`) // This boolean value is passed from the auth.js middleware. const proRateLimits = req.locals.proLimit From 010dceeb99fb021a45df1fd02c24f39eaff6f1aa Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Sun, 22 Dec 2019 16:33:29 +0000 Subject: [PATCH 41/49] chore(package): update sinon to version 8.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bad4f7f..1a5113f 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "request": "^2.88.0", "request-promise": "^4.2.2", "semantic-release": "^15.13.19", - "sinon": "^7.3.2", + "sinon": "^8.0.0", "typescript": "^3.1.4" }, "release": { From 59963a40c9ae00cba73759676583a7b216987806 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Sun, 22 Dec 2019 16:33:33 +0000 Subject: [PATCH 42/49] chore(package): update lockfile package-lock.json --- package-lock.json | 93 ++++++++++++++++++++++++++++++----------------- 1 file changed, 60 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index f5b6bb3..070034c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -607,33 +607,33 @@ } }, "@sinonjs/commons": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.6.0.tgz", - "integrity": "sha512-w4/WHG7C4WWFyE5geCieFJF6MZkbW4VAriol5KlmQXpAQdxvV0p26sqNZOW6Qyw6Y0l9K4g+cHvvczR2sEEpqg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.0.tgz", + "integrity": "sha512-qbk9AP+cZUsKdW1GJsBpxPKFmCJ0T8swwzVje3qFd+AkQb74Q/tiuzrdfFg8AD2g5HH/XbE/I8Uc1KYHVYWfhg==", "dev": true, "requires": { "type-detect": "4.0.8" } }, "@sinonjs/formatio": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", - "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-4.0.1.tgz", + "integrity": "sha512-asIdlLFrla/WZybhm0C8eEzaDNNrzymiTqHMeJl6zPW2881l3uuVRpm0QlRQEjqYWv6CcKMGYME3LbrLJsORBw==", "dev": true, "requires": { "@sinonjs/commons": "^1", - "@sinonjs/samsam": "^3.1.0" + "@sinonjs/samsam": "^4.2.0" } }, "@sinonjs/samsam": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", - "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-4.2.0.tgz", + "integrity": "sha512-yG7QbUz38ZPIegfuSMEcbOo0kkLGmPa8a0Qlz4dk7+cXYALDScWjIZzAm/u2+Frh+bcdZF6wZJZwwuJjY0WAjA==", "dev": true, "requires": { - "@sinonjs/commons": "^1.3.0", + "@sinonjs/commons": "^1.6.0", "array-from": "^2.1.1", - "lodash": "^4.17.15" + "lodash.get": "^4.4.2" } }, "@sinonjs/text-encoding": { @@ -7031,10 +7031,13 @@ } }, "lolex": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-4.2.0.tgz", - "integrity": "sha512-gKO5uExCXvSm6zbF562EvM+rd1kQDnB9AZBbiQVzf1ZmdDpxUSvpnAaVOP83N/31mRK8Ml8/VE8DMvsAZQ+7wg==", - "dev": true + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", + "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } }, "loose-envify": { "version": "1.4.0", @@ -7656,15 +7659,16 @@ "dev": true }, "nise": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.2.tgz", - "integrity": "sha512-/6RhOUlicRCbE9s+94qCUsyE+pKlVJ5AhIv+jEE7ESKwnbXqulKZ1FYU+XAtHHWE9TinYvAxDUJAb912PwPoWA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/nise/-/nise-3.0.0.tgz", + "integrity": "sha512-EObFx5tioBMePHpU/gGczaY2YDqL255iwjmZwswu2CiwEW8xIGrr3E2xij+efIppS1nLQo9NyXSIUySGHUOhHQ==", "dev": true, "requires": { - "@sinonjs/formatio": "^3.2.1", + "@sinonjs/commons": "^1.7.0", + "@sinonjs/formatio": "^4.0.1", "@sinonjs/text-encoding": "^0.7.1", "just-extend": "^4.0.2", - "lolex": "^4.1.0", + "lolex": "^5.0.1", "path-to-regexp": "^1.7.0" }, "dependencies": { @@ -7675,9 +7679,9 @@ "dev": true }, "path-to-regexp": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz", - "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", "dev": true, "requires": { "isarray": "0.0.1" @@ -13191,18 +13195,41 @@ } }, "sinon": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-7.5.0.tgz", - "integrity": "sha512-AoD0oJWerp0/rY9czP/D6hDTTUYGpObhZjMpd7Cl/A6+j0xBE+ayL/ldfggkBXUs0IkvIiM1ljM8+WkOc5k78Q==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-8.0.0.tgz", + "integrity": "sha512-9ugfO9tMrxTzqViG0hGhJoLXj8M01FZwfXMpYSmQT1AuV2jyXDJZUMpbENHBY9/2c2u6RKkHOcbYINx2FttUkg==", "dev": true, "requires": { "@sinonjs/commons": "^1.4.0", - "@sinonjs/formatio": "^3.2.1", - "@sinonjs/samsam": "^3.3.3", - "diff": "^3.5.0", - "lolex": "^4.2.0", - "nise": "^1.5.2", - "supports-color": "^5.5.0" + "@sinonjs/formatio": "^4.0.1", + "@sinonjs/samsam": "^4.0.1", + "diff": "^4.0.1", + "lolex": "^5.1.2", + "nise": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "diff": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", + "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "slice-ansi": { From cb20efc068660aca2c78b936ffb55da233a5624f Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2019 01:22:30 +0000 Subject: [PATCH 43/49] chore(package): update nyc to version 15.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bad4f7f..35214a9 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "nock": "^11.1.0", "node-mocks-http": "^1.7.0", "nodemon": "^1.18.1", - "nyc": "^14.1.1", + "nyc": "^15.0.0", "prettier": "^1.18.2", "proxyquire": "^2.1.0", "request": "^2.88.0", From cdc753f86976b75523746059dbfbe707bfe9ab58 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2019 01:22:37 +0000 Subject: [PATCH 44/49] chore(package): update lockfile package-lock.json --- package-lock.json | 860 +++++++++++++++++++++++++++++++++------------- 1 file changed, 630 insertions(+), 230 deletions(-) diff --git a/package-lock.json b/package-lock.json index f5b6bb3..d4fc8d2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,41 +12,79 @@ "@babel/highlight": "^7.0.0" } }, - "@babel/generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.4.tgz", - "integrity": "sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w==", + "@babel/core": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.7.tgz", + "integrity": "sha512-jlSjuj/7z138NLZALxVgrx13AOtqip42ATZP7+kYl53GvDV6+4dCek1mVUo8z8c8Xnw/mx2q3d9HWh3griuesQ==", "requires": { - "@babel/types": "^7.6.3", + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.7.7", + "@babel/helpers": "^7.7.4", + "@babel/parser": "^7.7.7", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "@babel/generator": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.7.7.tgz", + "integrity": "sha512-/AOIBpHh/JU1l0ZFS4kiRCBnLi6OTHzh0RPk3h9isBxkkqELtQNFi1Vr/tiG9p1yfoUdKVwISuXWQR+hwwM4VQ==", + "requires": { + "@babel/types": "^7.7.4", "jsesc": "^2.5.1", "lodash": "^4.17.13", "source-map": "^0.5.0" } }, "@babel/helper-function-name": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", - "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz", + "integrity": "sha512-AnkGIdiBhEuiwdoMnKm7jfPfqItZhgRaZfMg1XX3bS25INOnLPjPG1Ppnajh8eqgt5kPJnfqrRHqFqmjKDZLzQ==", "requires": { - "@babel/helper-get-function-arity": "^7.0.0", - "@babel/template": "^7.1.0", - "@babel/types": "^7.0.0" + "@babel/helper-get-function-arity": "^7.7.4", + "@babel/template": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/helper-get-function-arity": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", - "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.7.4.tgz", + "integrity": "sha512-QTGKEdCkjgzgfJ3bAyRwF4yyT3pg+vDgan8DSivq1eS0gwi+KGKE5x8kRcbeFTb/673mkO5SN1IZfmCfA5o+EA==", "requires": { - "@babel/types": "^7.0.0" + "@babel/types": "^7.7.4" } }, "@babel/helper-split-export-declaration": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", - "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz", + "integrity": "sha512-guAg1SXFcVr04Guk9eq0S4/rWS++sbmyqosJzVs8+1fH5NI+ZcmkaSkc7dmtAFbHFva6yRJnjW3yAcGxjueDug==", "requires": { - "@babel/types": "^7.4.4" + "@babel/types": "^7.7.4" + } + }, + "@babel/helpers": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.4.tgz", + "integrity": "sha512-ak5NGZGJ6LV85Q1Zc9gn2n+ayXOizryhjSUBTdu5ih1tlVCJeuQENzc4ItyCVhINVXvIT/ZQ4mheGIsfBkpskg==", + "requires": { + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/highlight": { @@ -60,40 +98,40 @@ } }, "@babel/parser": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.4.tgz", - "integrity": "sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==" + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.7.tgz", + "integrity": "sha512-WtTZMZAZLbeymhkd/sEaPD8IQyGAhmuTuvTzLiCFM7iXiVdY0gc0IaI+cW0fh1BnSMbJSzXX6/fHllgHKwHhXw==" }, "@babel/template": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", - "integrity": "sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz", + "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==", "requires": { "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.6.0", - "@babel/types": "^7.6.0" + "@babel/parser": "^7.7.4", + "@babel/types": "^7.7.4" } }, "@babel/traverse": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.3.tgz", - "integrity": "sha512-unn7P4LGsijIxaAJo/wpoU11zN+2IaClkQAxcJWBNCMS6cmVh802IyLHNkAjQ0iYnRS3nnxk5O3fuXW28IMxTw==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz", + "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==", "requires": { "@babel/code-frame": "^7.5.5", - "@babel/generator": "^7.6.3", - "@babel/helper-function-name": "^7.1.0", - "@babel/helper-split-export-declaration": "^7.4.4", - "@babel/parser": "^7.6.3", - "@babel/types": "^7.6.3", + "@babel/generator": "^7.7.4", + "@babel/helper-function-name": "^7.7.4", + "@babel/helper-split-export-declaration": "^7.7.4", + "@babel/parser": "^7.7.4", + "@babel/types": "^7.7.4", "debug": "^4.1.0", "globals": "^11.1.0", "lodash": "^4.17.13" } }, "@babel/types": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.3.tgz", - "integrity": "sha512-CqbcpTxMcpuQTMhjI37ZHVgjBkysg5icREQIEZ0eG1yCNwg3oy+5AaLiOKmjsCj6nqOsa6Hf0ObjRVwokb7srA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz", + "integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==", "requires": { "esutils": "^2.0.2", "lodash": "^4.17.13", @@ -148,6 +186,59 @@ "wif": "^2.0.6" } }, + "@istanbuljs/load-nyc-config": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", + "integrity": "sha512-ZR0rq/f/E4f4XcgnDvtMWXCUJpi8eO0rssVhmztsZqLIEFA9UUP9zmpE0VxlM+kv/E1ul2I876Fwil2ayptDVg==", + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", + "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==" + }, "@nodelib/fs.scandir": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", @@ -642,6 +733,11 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -754,7 +850,6 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz", "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==", - "dev": true, "requires": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" @@ -763,8 +858,7 @@ "indent-string": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==" } } }, @@ -934,11 +1028,11 @@ } }, "append-transform": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-1.0.0.tgz", - "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/append-transform/-/append-transform-2.0.0.tgz", + "integrity": "sha512-7yeyCEurROLQJFv5Xj4lEGTy0borxepjFv1g22oAdqFu//SrAlDl1O1Nxx15SH1RoliUml6p8dwJW9jvZughhg==", "requires": { - "default-require-extensions": "^2.0.0" + "default-require-extensions": "^3.0.0" } }, "archy": { @@ -1820,14 +1914,27 @@ } }, "caching-transform": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-3.0.2.tgz", - "integrity": "sha512-Mtgcv3lh3U0zRii/6qVgQODdPA4G3zhG+jtbCWj39RXuUFTMzH0vcdMtaJS1jPowd+It2Pqr6y3NJMQqOqCE2w==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", + "integrity": "sha512-kpqOvwXnjjN44D89K5ccQC+RUrsy7jB/XLlRrx0D7/2HNcTPqzsb6XgYoErwko6QsV184CA2YgS1fxDiiDZMWA==", "requires": { - "hasha": "^3.0.0", - "make-dir": "^2.0.0", - "package-hash": "^3.0.0", - "write-file-atomic": "^2.4.2" + "hasha": "^5.0.0", + "make-dir": "^3.0.0", + "package-hash": "^4.0.0", + "write-file-atomic": "^3.0.0" + }, + "dependencies": { + "write-file-atomic": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.1.tgz", + "integrity": "sha512-JPStrIyyVJ6oCSz/691fAjFtefZ6q+fP6tm+OS4Qw6o+TGQxNp1ziY2PgS+X/m0V8OWhZiO/m4xSj+Pr4RrZvw==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + } } }, "callback-stream": { @@ -2081,8 +2188,7 @@ "clean-stack": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "dev": true + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" }, "clear": { "version": "0.1.0", @@ -2517,9 +2623,9 @@ } }, "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", "requires": { "safe-buffer": "~5.1.1" }, @@ -2692,12 +2798,41 @@ } }, "cross-spawn": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-4.0.2.tgz", - "integrity": "sha1-e5JHYhwjrf3ThWAEqCPL45dCTUE=", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", + "integrity": "sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg==", "requires": { - "lru-cache": "^4.0.1", - "which": "^1.2.9" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "dependencies": { + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + } } }, "crypto-random-string": { @@ -2812,11 +2947,18 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, "default-require-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-2.0.0.tgz", - "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/default-require-extensions/-/default-require-extensions-3.0.0.tgz", + "integrity": "sha512-ek6DpXq/SCpvjhpFsLFRVtIxJCRw6fUR42lYMVZuUMK7n8eMz4Uh5clckdBjEpLhn/gEBZo7hDJnJcwdKLKQjg==", "requires": { - "strip-bom": "^3.0.0" + "strip-bom": "^4.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" + } } }, "deferred-leveldown": { @@ -4676,13 +4818,13 @@ } }, "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.2.0.tgz", + "integrity": "sha512-1JKclkYYsf1q9WIJKLZa9S9muC+08RIjzAlLrK4QcYLJMS6mk9yombQ9qf+zJ7H9LS800k0s44L4sDq9VYzqyg==", "requires": { "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" + "make-dir": "^3.0.0", + "pkg-dir": "^4.1.0" } }, "find-root": { @@ -4781,12 +4923,12 @@ "dev": true }, "foreground-child": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-1.5.6.tgz", - "integrity": "sha1-T9ca0t/elnibmApcCilZN8svXOk=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-2.0.0.tgz", + "integrity": "sha512-dCIq9FpEcyQyXKCkyzmlPTFNgrCzPudOe+mhvJU5zAtlBnGVy2yKxtfsxK2tQBThwq225jcvBjpw1Gr40uzZCA==", "requires": { - "cross-spawn": "^4", - "signal-exit": "^3.0.0" + "cross-spawn": "^7.0.0", + "signal-exit": "^3.0.2" } }, "forever-agent": { @@ -4872,6 +5014,11 @@ } } }, + "fromentries": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.2.0.tgz", + "integrity": "sha512-33X7H/wdfO99GdRLLgkjUrD4geAFdq/Uv0kl3HD4da6HDixd2GUg8Mw7dahLCV9r/EARkmtYBB6Tch4EEokFTQ==" + }, "fs-extra": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", @@ -5669,6 +5816,7 @@ "version": "4.5.3", "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", + "dev": true, "requires": { "neo-async": "^2.6.0", "optimist": "^0.6.1", @@ -5679,7 +5827,8 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true } } }, @@ -5835,11 +5984,24 @@ } }, "hasha": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-3.0.0.tgz", - "integrity": "sha1-UqMvq4Vp1BymmmH/GiFPjrfIvTk=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/hasha/-/hasha-5.1.0.tgz", + "integrity": "sha512-OFPDWmzPN1l7atOV1TgBVmNtBxaIysToK6Ve9DK+vT6pYuklw/nPNT+HJbZi0KDcI6vWB+9tgvZ5YD7fA3CXcA==", "requires": { - "is-stream": "^1.0.1" + "is-stream": "^2.0.0", + "type-fest": "^0.8.0" + }, + "dependencies": { + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + } } }, "he": { @@ -5949,6 +6111,11 @@ } } }, + "html-escaper": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.0.tgz", + "integrity": "sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig==" + }, "http-errors": { "version": "1.7.2", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", @@ -6507,8 +6674,7 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "is-unc-path": { "version": "1.0.0", @@ -6559,30 +6725,30 @@ } }, "istanbul-lib-coverage": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", - "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", + "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==" }, "istanbul-lib-hook": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-2.0.7.tgz", - "integrity": "sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-hook/-/istanbul-lib-hook-3.0.0.tgz", + "integrity": "sha512-Pt/uge1Q9s+5VAZ+pCo16TYMWPBIl+oaNIjgLQxcX0itS6ueeaA+pEfThZpH8WxhFgCiEb8sAJY6MdUKgiIWaQ==", "requires": { - "append-transform": "^1.0.0" + "append-transform": "^2.0.0" } }, "istanbul-lib-instrument": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", - "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.0.tgz", + "integrity": "sha512-Nm4wVHdo7ZXSG30KjZ2Wl5SU/Bw7bDx1PdaiIFzEStdjs0H12mOTncn1GVYuqQSaZxpg87VGBRsVRPGD2cD1AQ==", "requires": { - "@babel/generator": "^7.4.0", - "@babel/parser": "^7.4.3", - "@babel/template": "^7.4.0", - "@babel/traverse": "^7.4.3", - "@babel/types": "^7.4.0", - "istanbul-lib-coverage": "^2.0.5", - "semver": "^6.0.0" + "@babel/core": "^7.7.5", + "@babel/parser": "^7.7.5", + "@babel/template": "^7.7.4", + "@babel/traverse": "^7.7.4", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.0.0", + "semver": "^6.3.0" }, "dependencies": { "semver": { @@ -6592,35 +6758,62 @@ } } }, - "istanbul-lib-report": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", - "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "istanbul-lib-processinfo": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-processinfo/-/istanbul-lib-processinfo-2.0.2.tgz", + "integrity": "sha512-kOwpa7z9hme+IBPZMzQ5vdQj8srYgAtaRqeI48NGmAQ+/5yKiHLV0QbYqQpxsdEF0+w14SoB8YbnHKcXE2KnYw==", "requires": { - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "supports-color": "^6.1.0" + "archy": "^1.0.0", + "cross-spawn": "^7.0.0", + "istanbul-lib-coverage": "^3.0.0-alpha.1", + "make-dir": "^3.0.0", + "p-map": "^3.0.0", + "rimraf": "^3.0.0", + "uuid": "^3.3.3" }, "dependencies": { - "supports-color": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", - "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", "requires": { - "has-flag": "^3.0.0" + "aggregate-error": "^3.0.0" + } + } + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "requires": { + "has-flag": "^4.0.0" } } } }, "istanbul-lib-source-maps": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", - "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", + "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", "requires": { "debug": "^4.1.1", - "istanbul-lib-coverage": "^2.0.5", - "make-dir": "^2.1.0", - "rimraf": "^2.6.3", + "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" }, "dependencies": { @@ -6632,11 +6825,12 @@ } }, "istanbul-reports": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", - "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-2osTcC8zcOSUkImzN2EWQta3Vdi4WjjKw99P2yWx5mLnigAM0Rd5uYFn1cf2i/Ois45GkNjaoTqc5CxgMSX80A==", "requires": { - "handlebars": "^4.1.2" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" } }, "java-properties": { @@ -6702,6 +6896,21 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", "dev": true }, + "json5": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", + "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "requires": { + "minimist": "^1.2.0" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + } + } + }, "jsonfile": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", @@ -7081,18 +7290,17 @@ "dev": true }, "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.0.0.tgz", + "integrity": "sha512-grNJDhb8b1Jm1qeqW5R/O63wUo4UXo2v2HMic6YT9i/HBlF93S8jkMgH7yugvY9ABDShH4VZMn8I+U8+fCNegw==", "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" + "semver": "^6.0.0" }, "dependencies": { "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" } } }, @@ -7252,21 +7460,6 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" }, - "merge-source-map": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz", - "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==", - "requires": { - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - } - } - }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -7354,7 +7547,8 @@ "minimist": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true }, "minimist-options": { "version": "3.0.2", @@ -7631,7 +7825,8 @@ "neo-async": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==" + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true }, "nerf-dart": { "version": "1.0.0", @@ -7761,6 +7956,14 @@ "type-is": "^1.6.18" } }, + "node-preload": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-preload/-/node-preload-0.2.1.tgz", + "integrity": "sha512-RM5oyBy45cLEoHqCeh+MNuFAxO0vTFBLskvQbOKnEE7YTTSN4tbN8QWDIPQ6L+WvKsB/qLEGpYe2ZZ9d4W9OIQ==", + "requires": { + "process-on-spawn": "^1.0.0" + } + }, "nodemon": { "version": "1.19.4", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.19.4.tgz", @@ -11322,47 +11525,196 @@ } }, "nyc": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/nyc/-/nyc-14.1.1.tgz", - "integrity": "sha512-OI0vm6ZGUnoGZv/tLdZ2esSVzDwUC88SNs+6JoSOMVxA+gKMB8Tk7jBwgemLx4O40lhhvZCVw1C+OYLOBOPXWw==", + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.0.0.tgz", + "integrity": "sha512-qcLBlNCKMDVuKb7d1fpxjPR8sHeMVX0CHarXAVzrVWoFrigCkYR8xcrjfXSPi5HXM7EU78L6ywO7w1c5rZNCNg==", "requires": { - "archy": "^1.0.0", - "caching-transform": "^3.0.2", - "convert-source-map": "^1.6.0", - "cp-file": "^6.2.0", - "find-cache-dir": "^2.1.0", - "find-up": "^3.0.0", - "foreground-child": "^1.5.6", - "glob": "^7.1.3", - "istanbul-lib-coverage": "^2.0.5", - "istanbul-lib-hook": "^2.0.7", - "istanbul-lib-instrument": "^3.3.0", - "istanbul-lib-report": "^2.0.8", - "istanbul-lib-source-maps": "^3.0.6", - "istanbul-reports": "^2.2.4", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "caching-transform": "^4.0.0", + "convert-source-map": "^1.7.0", + "decamelize": "^1.2.0", + "find-cache-dir": "^3.2.0", + "find-up": "^4.1.0", + "foreground-child": "^2.0.0", + "glob": "^7.1.6", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-hook": "^3.0.0", + "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-processinfo": "^2.0.2", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.0.0", "js-yaml": "^3.13.1", - "make-dir": "^2.1.0", - "merge-source-map": "^1.1.0", - "resolve-from": "^4.0.0", - "rimraf": "^2.6.3", + "make-dir": "^3.0.0", + "node-preload": "^0.2.0", + "p-map": "^3.0.0", + "process-on-spawn": "^1.0.0", + "resolve-from": "^5.0.0", + "rimraf": "^3.0.0", "signal-exit": "^3.0.2", - "spawn-wrap": "^1.4.2", - "test-exclude": "^5.2.3", - "uuid": "^3.3.2", - "yargs": "^13.2.2", - "yargs-parser": "^13.0.0" + "spawn-wrap": "^2.0.0", + "test-exclude": "^6.0.0", + "uuid": "^3.3.3", + "yargs": "^15.0.2" }, "dependencies": { - "cp-file": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-6.2.0.tgz", - "integrity": "sha512-fmvV4caBnofhPe8kOcitBwSn2f39QLjnAnGq3gO9dfd75mUytzKNZB1hde6QHunW2Rt+OwuBOMc3i1tNElbszA==", + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.0.tgz", + "integrity": "sha512-7kFQgnEaMdRtwf6uSfUnVr9gSGC7faurn+J/Mv90/W+iTtN0405/nLdopfMWwchyxhbGYl6TC4Sccn9TUkGAgg==", "requires": { - "graceful-fs": "^4.1.2", - "make-dir": "^2.0.0", - "nested-error-stacks": "^2.0.0", - "pify": "^4.0.1", - "safe-buffer": "^5.0.1" + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-map": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-3.0.0.tgz", + "integrity": "sha512-d3qXVTF/s+W+CdJ5A29wywV2n8CQQYahlgz2bFiA+4eVNJbHJodPZ+/gXwPGh0bOqA+j8S+6+ckmvLGPk1QpxQ==", + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==" + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "yargs": { + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.0.2.tgz", + "integrity": "sha512-GH/X/hYt+x5hOat4LMnCqMd8r5Cv78heOMIJn1hr7QPPBqfeC6p89Y78+WB9yGDvfpCvgasfmWLzNzEioOUD9Q==", + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^16.1.0" + } + }, + "yargs-parser": { + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-16.1.0.tgz", + "integrity": "sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" } } } @@ -11538,6 +11890,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, "requires": { "minimist": "~0.0.1", "wordwrap": "~0.0.2" @@ -11600,11 +11953,6 @@ } } }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" - }, "os-name": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", @@ -11700,12 +12048,12 @@ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, "package-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-3.0.0.tgz", - "integrity": "sha512-lOtmukMDVvtkL84rJHI7dpTYq+0rli8N2wlnqUcBuDWCfVhRUfOmnR9SsoHFMLpACvEV60dX7rd0rFaYDZI+FA==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/package-hash/-/package-hash-4.0.0.tgz", + "integrity": "sha512-whdkPIooSu/bASggZ96BWVvZTRMOFxnyUG5PnTSGKoJE2gd5mbVNmR2Nj20QFzxYYgAXpoqC+AiXzl+UMRh7zQ==", "requires": { "graceful-fs": "^4.1.15", - "hasha": "^3.0.0", + "hasha": "^5.0.0", "lodash.flattendeep": "^4.4.0", "release-zalgo": "^1.0.0" } @@ -11850,6 +12198,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, "requires": { "pify": "^3.0.0" }, @@ -11857,7 +12206,8 @@ "pify": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true } } }, @@ -11960,11 +12310,6 @@ "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", "dev": true }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==" - }, "pkg-conf": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", @@ -12025,11 +12370,43 @@ } }, "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "requires": { - "find-up": "^3.0.0" + "find-up": "^4.0.0" + }, + "dependencies": { + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "requires": { + "p-limit": "^2.2.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" + } } }, "pluralize": { @@ -12102,6 +12479,14 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "process-on-spawn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", + "integrity": "sha512-1WsPDsUSMmZH5LeMLegqkPDrsGgsWwk1Exipy2hvB0o/F0ASzbpIctSCcZIK1ykJvtTJULEH+20WOFjMvGnCTg==", + "requires": { + "fromentries": "^1.2.0" + } + }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -12307,21 +12692,13 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, "requires": { "load-json-file": "^4.0.0", "normalize-package-data": "^2.3.2", "path-type": "^3.0.0" } }, - "read-pkg-up": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", - "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", - "requires": { - "find-up": "^3.0.0", - "read-pkg": "^3.0.0" - } - }, "readable-stream": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", @@ -12563,7 +12940,8 @@ "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true }, "resolve-url": { "version": "0.2.1", @@ -12607,9 +12985,9 @@ "dev": true }, "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.0.tgz", + "integrity": "sha512-NDGVxTsjqfunkds7CqsOiEnxln4Bo7Nddl3XhS4pXg5OzwkLqJ971ZVAAnB+DDLnF76N+VnDEiBHaVV8I06SUg==", "requires": { "glob": "^7.1.3" } @@ -13460,16 +13838,26 @@ "dev": true }, "spawn-wrap": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-1.4.3.tgz", - "integrity": "sha512-IgB8md0QW/+tWqcavuFgKYR/qIRvJkRLPJDFaoXtLLUaVcCDK0+HeFTkmQHj3eprcYhc+gOl0aEA1w7qZlYezw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/spawn-wrap/-/spawn-wrap-2.0.0.tgz", + "integrity": "sha512-EeajNjfN9zMnULLwhZZQU3GWBoFNkbngTUPfaawT4RkMiviTxcX0qfhVbGey39mfctfDHkWtuecgQ8NJcyQWHg==", "requires": { - "foreground-child": "^1.5.6", - "mkdirp": "^0.5.0", - "os-homedir": "^1.0.1", - "rimraf": "^2.6.2", + "foreground-child": "^2.0.0", + "is-windows": "^1.0.2", + "make-dir": "^3.0.0", + "rimraf": "^3.0.0", "signal-exit": "^3.0.2", - "which": "^1.3.0" + "which": "^2.0.1" + }, + "dependencies": { + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "requires": { + "isexe": "^2.0.0" + } + } } }, "spdx-correct": { @@ -13972,14 +14360,13 @@ } }, "test-exclude": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", - "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "requires": { - "glob": "^7.1.3", - "minimatch": "^3.0.4", - "read-pkg-up": "^4.0.0", - "require-main-filename": "^2.0.0" + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" } }, "text-extensions": { @@ -14245,6 +14632,14 @@ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, "typeforce": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/typeforce/-/typeforce-1.18.0.tgz", @@ -14265,6 +14660,7 @@ "version": "3.6.5", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.5.tgz", "integrity": "sha512-7L3W+Npia1OCr5Blp4/Vw83tK1mu5gnoIURtT1fUVfQ3Kf8WStWV6NJz0fdoBJZls0KlweruRTLVe6XLafmy5g==", + "dev": true, "optional": true, "requires": { "commander": "~2.20.3", @@ -14275,12 +14671,14 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, "optional": true }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, "optional": true } } @@ -14825,7 +15223,8 @@ "wordwrap": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true }, "wrap-ansi": { "version": "5.1.0", @@ -14854,6 +15253,7 @@ "version": "2.4.3", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "dev": true, "requires": { "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", From 6efcd9d80459b2e2ea0c1522bfb1d11b654d4940 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 23 Dec 2019 10:45:09 -0800 Subject: [PATCH 45/49] Adding script to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index ef6da67..15e6fa1 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ start-decatur-main start-decatur-test start-bitcoincom.sh test-remote-server.sh +start-bitcoincom-testnet.sh coverage start-my-infra @@ -16,3 +17,4 @@ nohup.out slp-tx-db/ logs/ docs/* + From 22245c47c8721a1b7781168a327994c9ac7e7320 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 23 Dec 2019 12:11:37 -0800 Subject: [PATCH 46/49] Updating gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 15e6fa1..dfc88cb 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ start-decatur-test start-bitcoincom.sh test-remote-server.sh start-bitcoincom-testnet.sh +start-local-server.sh +start-local-testnet-server.sh coverage start-my-infra From 09ee4e4de084b66dd103533bf2d4480b16a5bd7d Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2020 21:00:58 +0000 Subject: [PATCH 47/49] chore(package): update semantic-release to version 16.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bad4f7f..cf9a3d6 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,7 @@ "proxyquire": "^2.1.0", "request": "^2.88.0", "request-promise": "^4.2.2", - "semantic-release": "^15.13.19", + "semantic-release": "^16.0.0", "sinon": "^7.3.2", "typescript": "^3.1.4" }, From 8c28a53b3a65a186b7e6287c1c732008c6886460 Mon Sep 17 00:00:00 2001 From: "greenkeeper[bot]" <23040076+greenkeeper[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2020 21:01:07 +0000 Subject: [PATCH 48/49] chore(package): update lockfile package-lock.json --- package-lock.json | 766 +++++++++++++++++++++++++--------------------- 1 file changed, 419 insertions(+), 347 deletions(-) diff --git a/package-lock.json b/package-lock.json index f5b6bb3..5800567 100644 --- a/package-lock.json +++ b/package-lock.json @@ -64,6 +64,15 @@ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.4.tgz", "integrity": "sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A==" }, + "@babel/runtime": { + "version": "7.7.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.7.tgz", + "integrity": "sha512-uCnC2JEVAu8AKB5do1WRIsvrdJ0flYx/A/9f/6chdacnEZ7LmavjdsDXr5ksYBegxtuTPR5Va9/+13QF/kFkCA==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, "@babel/template": { "version": "7.6.0", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", @@ -175,12 +184,12 @@ } }, "@octokit/endpoint": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.5.0.tgz", - "integrity": "sha512-TXYS6zXeBImNB9BVj+LneMDqXX+H0exkOpyXobvp92O3B1348QsKnNioISFKgOMsb3ibZvQGwCdpiwQd3KAjIA==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.5.1.tgz", + "integrity": "sha512-nBFhRUb5YzVTCX/iAK1MgQ4uWo89Gu0TH00qQHoYRCsE12dWcG1OiLd7v2EIo2+tpUKPMOQ62QFy9hy9Vg2ULg==", "dev": true, "requires": { - "@octokit/types": "^1.0.0", + "@octokit/types": "^2.0.0", "is-plain-object": "^3.0.0", "universal-user-agent": "^4.0.0" }, @@ -203,14 +212,14 @@ } }, "@octokit/request": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.3.0.tgz", - "integrity": "sha512-mMIeNrtYyNEIYNsKivDyUAukBkw0M5ckyJX56xoFRXSasDPCloIXaQOnaKNopzQ8dIOvpdq1ma8gmrS+h6O2OQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.3.1.tgz", + "integrity": "sha512-5/X0AL1ZgoU32fAepTfEoggFinO3rxsMLtzhlUX+RctLrusn/CApJuGFCd0v7GMFhF+8UiCsTTfsu7Fh1HnEJg==", "dev": true, "requires": { "@octokit/endpoint": "^5.5.0", "@octokit/request-error": "^1.0.1", - "@octokit/types": "^1.0.0", + "@octokit/types": "^2.0.0", "deprecation": "^2.0.0", "is-plain-object": "^3.0.0", "node-fetch": "^2.3.0", @@ -236,19 +245,20 @@ } }, "@octokit/request-error": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.0.4.tgz", - "integrity": "sha512-L4JaJDXn8SGT+5G0uX79rZLv0MNJmfGa4vb4vy1NnpjSnWDLJRy6m90udGwvMmavwsStgbv2QNkPzzTCMmL+ig==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.0.tgz", + "integrity": "sha512-DNBhROBYjjV/I9n7A8kVkmQNkqFAMem90dSxqvPq57e2hBr7mNTX98y3R2zDpqMQHVRpBDjsvsfIGgBzy+4PAg==", "dev": true, "requires": { + "@octokit/types": "^2.0.0", "deprecation": "^2.0.0", "once": "^1.4.0" } }, "@octokit/rest": { - "version": "16.34.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.34.0.tgz", - "integrity": "sha512-EBe5qMQQOZRuezahWCXCnSe0J6tAqrW2hrEH9U8esXzKor1+HUDf8jgImaZf5lkTyWCQA296x9kAH5c0pxEgVQ==", + "version": "16.36.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.36.0.tgz", + "integrity": "sha512-zoZj7Ya4vWBK4fjTwK2Cnmu7XBB1p9ygSvTk2TthN6DVJXM4hQZQoAiknWFLJWSTix4dnA3vuHtjPZbExYoCZA==", "dev": true, "requires": { "@octokit/request": "^5.2.0", @@ -266,34 +276,27 @@ } }, "@octokit/types": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-1.1.0.tgz", - "integrity": "sha512-t4ZD74UnNVMq6kZBDZceflRKK3q4o5PoCKMAGht0RK84W57tqonqKL3vCxJHtbGExdan9RwV8r7VJBZxIM1O7Q==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.0.2.tgz", + "integrity": "sha512-StASIL2lgT3TRjxv17z9pAqbnI7HGu9DrJlg3sEBFfCLaMEqp+O3IQPUF6EZtQ4xkAu2ml6kMBBCtGxjvmtmuQ==", "dev": true, "requires": { - "@types/node": "^12.11.1" - }, - "dependencies": { - "@types/node": { - "version": "12.12.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.3.tgz", - "integrity": "sha512-opgSsy+cEF9N8MgaVPnWVtdJ3o4mV2aMHvDq7thkQUFt0EuOHJon4rQpJfhjmNHB+ikl0Cd6WhWIErOyQ+f7tw==", - "dev": true - } + "@types/node": ">= 8" } }, "@semantic-release/commit-analyzer": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-6.3.2.tgz", - "integrity": "sha512-TezpOODuMcdDuGcGsI2QRqkH3sT7ffhsxid9J8OyspXDe8E4tshekJESJ6Pq8jTrggluKd3tCK26Vsyu6SkeSg==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-7.0.0.tgz", + "integrity": "sha512-t5wMGByv+SknjP2m3rhWN4vmXoQ16g5VFY8iC4/tcbLPvzxK+35xsTIeUsrVFZv3ymdgAQKIr5J3lKjhF/VZZQ==", "dev": true, "requires": { "conventional-changelog-angular": "^5.0.0", "conventional-commits-filter": "^2.0.0", - "conventional-commits-parser": "^3.0.0", + "conventional-commits-parser": "^3.0.7", "debug": "^4.0.0", "import-from": "^3.0.0", - "lodash": "^4.17.4" + "lodash": "^4.17.4", + "micromatch": "^3.1.10" } }, "@semantic-release/error": { @@ -303,9 +306,9 @@ "dev": true }, "@semantic-release/github": { - "version": "5.5.5", - "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-5.5.5.tgz", - "integrity": "sha512-Wo9OIULMRydbq+HpFh9yiLvra1XyEULPro9Tp4T5MQJ0WZyAQ3YQm74IdT8Pe/UmVDq2nfpT1oHrWkwOc4loHg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/github/-/github-6.0.0.tgz", + "integrity": "sha512-lqRpCHxPAWTcl9QtDMH3c6UaB6vh3WDd7MqbmBubeHyVw4Nhxb1EpoWAlTMbUn1LEG9JzVZWez13ZFCjIdY6bA==", "dev": true, "requires": { "@octokit/rest": "^16.27.0", @@ -316,8 +319,8 @@ "dir-glob": "^3.0.0", "fs-extra": "^8.0.0", "globby": "^10.0.0", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^3.0.0", + "http-proxy-agent": "^3.0.0", + "https-proxy-agent": "^4.0.0", "issue-parser": "^5.0.0", "lodash": "^4.17.4", "mime": "^2.4.3", @@ -335,14 +338,14 @@ } }, "@semantic-release/npm": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-5.3.4.tgz", - "integrity": "sha512-XjITNRA/oOpJ7BfHk/WaOHs1WniYBszTde/bwADjjk1Luacpxg87jbDQVVt/oA3Zlx+MelxACRIEuRiPC5gu8g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@semantic-release/npm/-/npm-6.0.0.tgz", + "integrity": "sha512-aqODzbtWpVHO/keinbBMnZEaN/TkdwQvyDWcT0oNbKFpZwLjNjn+QVItoLekF62FLlXXziu2y6V4wnl9FDnujA==", "dev": true, "requires": { "@semantic-release/error": "^2.2.0", "aggregate-error": "^3.0.0", - "execa": "^3.2.0", + "execa": "^4.0.0", "fs-extra": "^8.0.0", "lodash": "^4.17.15", "nerf-dart": "^1.0.0", @@ -351,6 +354,7 @@ "rc": "^1.2.8", "read-pkg": "^5.0.0", "registry-auth-token": "^4.0.0", + "semver": "^6.3.0", "tempy": "^0.3.0" }, "dependencies": { @@ -366,9 +370,9 @@ } }, "execa": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz", - "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.0.tgz", + "integrity": "sha512-JbDUxwV3BoT5ZVXQrSVbAiaXhXUkIwvbhPIwZ0N13kX+5yCzOhUNdocxB/UQRuYOHRYYwAxKYwJYc0T4D12pDA==", "dev": true, "requires": { "cross-spawn": "^7.0.0", @@ -378,7 +382,6 @@ "merge-stream": "^2.0.0", "npm-run-path": "^4.0.0", "onetime": "^5.1.0", - "p-finally": "^2.0.0", "signal-exit": "^3.0.2", "strip-final-newline": "^2.0.0" } @@ -405,9 +408,9 @@ "dev": true }, "npm-run-path": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.0.tgz", - "integrity": "sha512-8eyAOAH+bYXFPSnNnKr3J+yoybe8O87Is5rtAQ8qRczJz1ajcsjg8l2oZqP+Ppx15Ii3S1vUTjQN2h4YO2tWWQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { "path-key": "^3.0.0" @@ -422,12 +425,6 @@ "mimic-fn": "^2.1.0" } }, - "p-finally": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-2.0.1.tgz", - "integrity": "sha512-vpm09aKwq6H9phqRQzecoDpD8TmVyGw70qmWlyq5onxY7tqyTTFVvxMykxQSQKILBSFlbXpypIw2T1Ml7+DDtw==", - "dev": true - }, "parse-json": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", @@ -468,6 +465,12 @@ "safe-buffer": "^5.0.1" } }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -495,9 +498,9 @@ } }, "@semantic-release/release-notes-generator": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-7.3.2.tgz", - "integrity": "sha512-vYGydZPoQqL4aJOsaqXTZIekRb3aa/OlxlEVUvyrWWlNGqmQ1T7NUOos9eoN5DBCEuk6PwDrxPbhzgswxcvprQ==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/@semantic-release/release-notes-generator/-/release-notes-generator-7.3.5.tgz", + "integrity": "sha512-LGjgPBGjjmjap/76O0Md3wc04Y7IlLnzZceLsAkcYRwGQdRPTTFUJKqDQTuieWTs7zfHzQoZqsqPfFxEN+g2+Q==", "dev": true, "requires": { "conventional-changelog-angular": "^5.0.0", @@ -588,9 +591,9 @@ } }, "read-pkg-up": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.0.tgz", - "integrity": "sha512-t2ODkS/vTTcRlKwZiZsaLGb5iwfx9Urp924aGzVyboU6+7Z2i6eGr/G1Z4mjvwLLQV3uFOBKobNRGM3ux2PD/w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", "dev": true, "requires": { "find-up": "^4.1.0", @@ -642,6 +645,12 @@ "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", "dev": true }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -676,6 +685,12 @@ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", "dev": true }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, "@types/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", @@ -742,13 +757,10 @@ "integrity": "sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8=" }, "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "dev": true, - "requires": { - "es6-promisify": "^5.0.0" - } + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz", + "integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g==", + "dev": true }, "aggregate-error": { "version": "3.0.1", @@ -1016,12 +1028,6 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "array-uniq": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-2.1.0.tgz", - "integrity": "sha512-bdHxtev7FN6+MXI1YFW0Q8mQ8dTJc2S8AMfju+ZR77pbg2yAdVyDlwkaUI7Har0LyOMRFPHrJ9lYdyjZZswdlQ==", - "dev": true - }, "array-unique": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", @@ -1868,23 +1874,6 @@ } } }, - "caller-callsite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", - "integrity": "sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ=", - "dev": true, - "requires": { - "callsites": "^2.0.0" - }, - "dependencies": { - "callsites": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", - "integrity": "sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=", - "dev": true - } - } - }, "caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", @@ -2385,9 +2374,9 @@ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" }, "conventional-changelog-angular": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.5.tgz", - "integrity": "sha512-RrkdWnL/TVyWV1ayWmSsrWorsTDqjL/VwG5ZSEneBQrd65ONcfeA1cW7FLtNweQyMiKOyriCMTKRSlk18DjTrw==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.6.tgz", + "integrity": "sha512-QDEmLa+7qdhVIv8sFZfVxU1VSyVvnXPsxq8Vam49mKUcO1Z8VTLEJk9uI21uiJUsnmm0I4Hrsdc9TgkOQo9WSA==", "dev": true, "requires": { "compare-func": "^1.3.1", @@ -2395,9 +2384,9 @@ } }, "conventional-changelog-writer": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.9.tgz", - "integrity": "sha512-2Y3QfiAM37WvDMjkVNaRtZgxVzWKj73HE61YQ/95T53yle+CRwTVSl6Gbv/lWVKXeZcM5af9n9TDVf0k7Xh+cw==", + "version": "4.0.11", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-4.0.11.tgz", + "integrity": "sha512-g81GQOR392I+57Cw3IyP1f+f42ME6aEkbR+L7v1FBBWolB0xkjKTeCWVguzRrp6UiT1O6gBpJbEy2eq7AnV1rw==", "dev": true, "requires": { "compare-func": "^1.3.1", @@ -2405,8 +2394,8 @@ "dateformat": "^3.0.0", "handlebars": "^4.4.0", "json-stringify-safe": "^5.0.1", - "lodash": "^4.2.1", - "meow": "^4.0.0", + "lodash": "^4.17.15", + "meow": "^5.0.0", "semver": "^6.0.0", "split": "^1.0.0", "through2": "^3.0.0" @@ -2440,24 +2429,24 @@ } }, "conventional-commits-parser": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.5.tgz", - "integrity": "sha512-qVz9+5JwdJzsbt7JbJ6P7NOXBGt8CyLFJYSjKAuPSgO+5UGfcsbk9EMR+lI8Unlvx6qwIc2YDJlrGIfay2ehNA==", + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.0.8.tgz", + "integrity": "sha512-YcBSGkZbYp7d+Cr3NWUeXbPDFUN6g3SaSIzOybi8bjHL5IJ5225OSCxJJ4LgziyEJ7AaJtE9L2/EU6H7Nt/DDQ==", "dev": true, "requires": { "JSONStream": "^1.0.4", - "is-text-path": "^2.0.0", - "lodash": "^4.2.1", - "meow": "^4.0.0", + "is-text-path": "^1.0.1", + "lodash": "^4.17.15", + "meow": "^5.0.0", "split2": "^2.0.0", "through2": "^3.0.0", "trim-off-newlines": "^1.0.0" }, "dependencies": { "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -2571,40 +2560,34 @@ } }, "cosmiconfig": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", - "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", + "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==", "dev": true, "requires": { - "import-fresh": "^2.0.0", - "is-directory": "^0.3.1", - "js-yaml": "^3.13.1", - "parse-json": "^4.0.0" + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.1.0", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.7.2" }, "dependencies": { - "caller-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", - "integrity": "sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ=", + "parse-json": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", + "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", "dev": true, "requires": { - "caller-callsite": "^2.0.0" + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1", + "lines-and-columns": "^1.1.6" } }, - "import-fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", - "integrity": "sha1-2BNVwVYS04bGH53dOSLUMEgipUY=", - "dev": true, - "requires": { - "caller-path": "^2.0.0", - "resolve-from": "^3.0.0" - } - }, - "resolve-from": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", - "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true } } @@ -3006,9 +2989,9 @@ }, "dependencies": { "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -3217,9 +3200,9 @@ "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" }, "env-ci": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-4.5.1.tgz", - "integrity": "sha512-Xtmr+ordf8POu3NcNzx3eOa2zHyfD4h3fPHX5fLklkWa86ck35n1c9oZmyUnVPUl9zHnpZWdWtCUBPSWEagjCQ==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/env-ci/-/env-ci-4.5.2.tgz", + "integrity": "sha512-lS+edpNp2+QXEPkx6raEMIjKxKKWnJ4+VWzovYJ2NLYiJAYenSAXotFfVdgaFxdbVnvAbUI8epQDa1u12ERxfQ==", "dev": true, "requires": { "execa": "^3.2.0", @@ -3238,9 +3221,9 @@ } }, "execa": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-3.2.0.tgz", - "integrity": "sha512-kJJfVbI/lZE1PZYDI5VPxp8zXPO9rtxOkhpZ0jMKha56AI9y2gGVC6bkukStQf0ka5Rh15BA5m7cCCH4jmHqkw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz", + "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==", "dev": true, "requires": { "cross-spawn": "^7.0.0", @@ -3277,9 +3260,9 @@ "dev": true }, "npm-run-path": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.0.tgz", - "integrity": "sha512-8eyAOAH+bYXFPSnNnKr3J+yoybe8O87Is5rtAQ8qRczJz1ajcsjg8l2oZqP+Ppx15Ii3S1vUTjQN2h4YO2tWWQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { "path-key": "^3.0.0" @@ -3301,9 +3284,9 @@ "dev": true }, "path-key": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.0.tgz", - "integrity": "sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "shebang-command": { @@ -3322,9 +3305,9 @@ "dev": true }, "which": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.1.tgz", - "integrity": "sha512-N7GBZOTswtB9lkQBZA4+zAXrjEIWAUOB93AvzUiudRzRxhUdLURQ7D/gAIMY1gatT/LTbmbcv8SiYazy3eYB7w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" @@ -3430,21 +3413,6 @@ "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", "integrity": "sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw=" }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==", - "dev": true - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "dev": true, - "requires": { - "es6-promise": "^4.0.3" - } - }, "es6-set": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", @@ -4469,9 +4437,9 @@ "dev": true }, "fast-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.0.tgz", - "integrity": "sha512-TrUz3THiq2Vy3bjfQUB2wNyPdGBeGmdjbzzBLhfHN4YFurYptCKwGq/TfiRavbGywFRzY6U2CdmQ1zmsY5yYaw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.1.1.tgz", + "integrity": "sha512-nTCREpBY8w8r+boyFYAx21iL6faSsQynliPHM4Uf56SbkyohCNxpVPEH9xrF5TXKy+IsjkPUHDKiUkzBVRXn9g==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -4699,12 +4667,11 @@ } }, "find-versions": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.1.0.tgz", - "integrity": "sha512-NCTfNiVzeE/xL+roNDffGuRbrWI6atI18lTJ22vKp7rs2OhYzMK3W1dIdO2TUndH/QMcacM4d1uWwgcZcHK69Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/find-versions/-/find-versions-3.2.0.tgz", + "integrity": "sha512-P8WRou2S+oe222TOCHitLy8zj+SIsVJh52VP4lvXkaFVnOFFdoWv1H1Jjvel1aI6NCFOAaeAVm8qrI0odiLcww==", "dev": true, "requires": { - "array-uniq": "^2.1.0", "semver-regex": "^2.0.0" } }, @@ -4841,9 +4808,9 @@ }, "dependencies": { "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -5607,9 +5574,9 @@ "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" }, "globby": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", - "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", "dev": true, "requires": { "@types/glob": "^7.1.1", @@ -5627,12 +5594,6 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz", "integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A==", "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true } } }, @@ -5969,30 +5930,13 @@ } }, "http-proxy-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-3.0.0.tgz", + "integrity": "sha512-uGuJaBWQWDQCJI5ip0d/VTYZW0nRrlLWXA4A7P1jrsa+f77rW2yXz315oBt6zGCF6l8C2tlMxY7ffULCj+5FhA==", "dev": true, "requires": { - "agent-base": "4", - "debug": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } + "agent-base": "5", + "debug": "4" } }, "http-signature": { @@ -6007,24 +5951,13 @@ } }, "https-proxy-agent": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz", - "integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz", + "integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==", "dev": true, "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } + "agent-base": "5", + "debug": "4" } }, "human-signals": { @@ -6318,12 +6251,6 @@ } } }, - "is-directory": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", - "integrity": "sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=", - "dev": true - }, "is-extendable": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", @@ -6496,12 +6423,12 @@ } }, "is-text-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz", - "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha1-Thqg+1G/vLPpJogAE5cgLBd1tm4=", "dev": true, "requires": { - "text-extensions": "^2.0.0" + "text-extensions": "^1.0.0" } }, "is-typedarray": { @@ -7170,22 +7097,28 @@ "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, "meow": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/meow/-/meow-4.0.1.tgz", - "integrity": "sha512-xcSBHD5Z86zaOc+781KrupuHAzeGXSLtiAOmBsiLDiPSaYSB6hdew2ng9EBAnZ62jagG9MHAOdxpDi/lWBFJ/A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-5.0.0.tgz", + "integrity": "sha512-CbTqYU17ABaLefO8vCU153ZZlprKYWDljcndKKDCFcYQITzWCXZAVk4QMFZPgvzrnUQ3uItnIE/LoUOwrT15Ig==", "dev": true, "requires": { "camelcase-keys": "^4.0.0", "decamelize-keys": "^1.0.0", "loud-rejection": "^1.0.0", - "minimist": "^1.1.3", "minimist-options": "^3.0.1", "normalize-package-data": "^2.3.4", "read-pkg-up": "^3.0.0", "redent": "^2.0.0", - "trim-newlines": "^2.0.0" + "trim-newlines": "^2.0.0", + "yargs-parser": "^10.0.0" }, "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, "find-up": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", @@ -7205,12 +7138,6 @@ "path-exists": "^3.0.0" } }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, "p-limit": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", @@ -7244,6 +7171,15 @@ "find-up": "^2.0.0", "read-pkg": "^3.0.0" } + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } } } }, @@ -7828,9 +7764,9 @@ "dev": true }, "npm": { - "version": "6.13.2", - "resolved": "https://registry.npmjs.org/npm/-/npm-6.13.2.tgz", - "integrity": "sha512-TG7AFkKpjBNJh8OVJYcGaAbW0PZxEkjew51Lc6TRdhQpNjSSEnAOEpidApqEuciB7cs09C8mxbo8NbuPs4QDzg==", + "version": "6.13.4", + "resolved": "https://registry.npmjs.org/npm/-/npm-6.13.4.tgz", + "integrity": "sha512-vTcUL4SCg3AzwInWTbqg1OIaOXlzKSS8Mb8kc5avwrJpcvevDA5J9BhYSuei+fNs3pwOp4lzA5x2FVDXACvoXA==", "dev": true, "requires": { "JSONStream": "^1.3.5", @@ -7839,7 +7775,7 @@ "ansistyles": "~0.1.3", "aproba": "^2.0.0", "archy": "~1.0.0", - "bin-links": "^1.1.3", + "bin-links": "^1.1.6", "bluebird": "^3.5.5", "byte-size": "^5.0.1", "cacache": "^12.0.3", @@ -7860,7 +7796,7 @@ "find-npm-prefix": "^1.0.2", "fs-vacuum": "~1.2.10", "fs-write-stream-atomic": "~1.0.10", - "gentle-fs": "^2.2.1", + "gentle-fs": "^2.3.0", "glob": "^7.1.4", "graceful-fs": "^4.2.3", "has-unicode": "~2.0.1", @@ -7909,7 +7845,7 @@ "npm-install-checks": "^3.0.2", "npm-lifecycle": "^3.1.4", "npm-package-arg": "^6.1.1", - "npm-packlist": "^1.4.6", + "npm-packlist": "^1.4.7", "npm-pick-manifest": "^3.0.2", "npm-profile": "^4.0.2", "npm-registry-fetch": "^4.0.2", @@ -7918,7 +7854,7 @@ "once": "~1.4.0", "opener": "^1.5.1", "osenv": "^0.1.5", - "pacote": "^9.5.9", + "pacote": "^9.5.11", "path-is-inside": "~1.0.2", "promise-inflight": "~1.0.1", "qrcode-terminal": "^0.12.0", @@ -7927,7 +7863,7 @@ "read": "~1.0.7", "read-cmd-shim": "^1.0.5", "read-installed": "~4.0.3", - "read-package-json": "^2.1.0", + "read-package-json": "^2.1.1", "read-package-tree": "^5.3.1", "readable-stream": "^3.4.0", "readdir-scoped-modules": "^1.1.0", @@ -8121,14 +8057,15 @@ } }, "bin-links": { - "version": "1.1.3", + "version": "1.1.6", "bundled": true, "dev": true, "requires": { "bluebird": "^3.5.3", "cmd-shim": "^3.0.0", - "gentle-fs": "^2.0.1", + "gentle-fs": "^2.3.0", "graceful-fs": "^4.1.15", + "npm-normalize-package-bin": "^1.0.0", "write-file-atomic": "^2.3.0" } }, @@ -8987,12 +8924,13 @@ "dev": true }, "gentle-fs": { - "version": "2.2.1", + "version": "2.3.0", "bundled": true, "dev": true, "requires": { "aproba": "^1.1.2", "chownr": "^1.1.2", + "cmd-shim": "^3.0.3", "fs-vacuum": "^1.2.10", "graceful-fs": "^4.1.11", "iferr": "^0.1.5", @@ -9959,9 +9897,12 @@ } }, "npm-bundled": { - "version": "1.0.6", + "version": "1.1.1", "bundled": true, - "dev": true + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } }, "npm-cache-filename": { "version": "1.0.2", @@ -9996,6 +9937,11 @@ "bundled": true, "dev": true }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, "npm-package-arg": { "version": "6.1.1", "bundled": true, @@ -10008,7 +9954,7 @@ } }, "npm-packlist": { - "version": "1.4.6", + "version": "1.4.7", "bundled": true, "dev": true, "requires": { @@ -10190,7 +10136,7 @@ } }, "pacote": { - "version": "9.5.9", + "version": "9.5.11", "bundled": true, "dev": true, "requires": { @@ -10208,6 +10154,7 @@ "mississippi": "^3.0.0", "mkdirp": "^0.5.1", "normalize-package-data": "^2.4.0", + "npm-normalize-package-bin": "^1.0.0", "npm-package-arg": "^6.1.0", "npm-packlist": "^1.1.12", "npm-pick-manifest": "^3.0.0", @@ -10481,7 +10428,7 @@ } }, "read-package-json": { - "version": "2.1.0", + "version": "2.1.1", "bundled": true, "dev": true, "requires": { @@ -10489,7 +10436,7 @@ "graceful-fs": "^4.1.2", "json-parse-better-errors": "^1.0.1", "normalize-package-data": "^2.0.0", - "slash": "^1.0.0" + "npm-normalize-package-bin": "^1.0.0" } }, "read-package-tree": { @@ -10664,11 +10611,6 @@ "bundled": true, "dev": true }, - "slash": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, "slide": { "version": "1.1.6", "bundled": true, @@ -11620,6 +11562,12 @@ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, + "p-each-series": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", + "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", + "dev": true + }, "p-event": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.1.0.tgz", @@ -11677,9 +11625,9 @@ "dev": true }, "p-retry": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.1.0.tgz", - "integrity": "sha512-oepllyG9gX1qH4Sm20YAKxg1GA7L7puhvGnTfimi31P07zSIj7SDV6YtuAx9nbJF51DES+2CIIRkXs8GKqWJxA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.2.0.tgz", + "integrity": "sha512-jPH38/MRh263KKcq0wBNOGFJbm+U6784RilTmHjB/HM9kH9V8WlCpVUcdOmip9cjXOh6MxZ5yk1z2SjDUJfWmA==", "dev": true, "requires": { "@types/retry": "^0.12.0", @@ -11955,9 +11903,9 @@ } }, "picomatch": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz", - "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", + "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==", "dev": true }, "pify": { @@ -12399,6 +12347,12 @@ "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.2.0.tgz", "integrity": "sha512-LgQJIuS6nAy1Jd88DCQRemyE3mS+ispwlqMk3b0yjZ257fI1v9c+/p6SD5gP5FGyXUIgrNOAfmyioHwZtYv2VA==" }, + "regenerator-runtime": { + "version": "0.13.3", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", + "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==", + "dev": true + }, "regex-not": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", @@ -12706,18 +12660,18 @@ } }, "semantic-release": { - "version": "15.13.28", - "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-15.13.28.tgz", - "integrity": "sha512-TqmRAsTFPq+hi0LjccUIHIknpW2wBPqVZWc/fVBBeeokuZ+wfGcP/Vo8nKci7VTHBl0D/EAu6gNvo343dKjWUA==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/semantic-release/-/semantic-release-16.0.0.tgz", + "integrity": "sha512-mwoZEIUXJPvRZFsUrPGDjr3weKMGFmO+b5wNdBbVKCMQArKZpWi99bqPJvAv7encTo9MIPPOKENB5SefEiv8xA==", "dev": true, "requires": { - "@semantic-release/commit-analyzer": "^6.1.0", + "@semantic-release/commit-analyzer": "^7.0.0", "@semantic-release/error": "^2.2.0", - "@semantic-release/github": "^5.1.0", - "@semantic-release/npm": "^5.0.5", + "@semantic-release/github": "^6.0.0", + "@semantic-release/npm": "^6.0.0", "@semantic-release/release-notes-generator": "^7.1.2", "aggregate-error": "^3.0.0", - "cosmiconfig": "^5.0.1", + "cosmiconfig": "^6.0.0", "debug": "^4.0.0", "env-ci": "^4.0.0", "execa": "^3.2.0", @@ -12730,15 +12684,68 @@ "lodash": "^4.17.15", "marked": "^0.7.0", "marked-terminal": "^3.2.0", - "p-locate": "^4.0.0", + "micromatch": "^4.0.2", + "p-each-series": "^2.1.0", "p-reduce": "^2.0.0", "read-pkg-up": "^7.0.0", "resolve-from": "^5.0.0", "semver": "^6.0.0", + "semver-diff": "^3.1.1", "signale": "^1.2.1", - "yargs": "^14.0.0" + "yargs": "^15.0.1" }, "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "cross-spawn": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.1.tgz", @@ -12750,10 +12757,16 @@ "which": "^2.0.1" } }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "execa": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-3.2.0.tgz", - "integrity": "sha512-kJJfVbI/lZE1PZYDI5VPxp8zXPO9rtxOkhpZ0jMKha56AI9y2gGVC6bkukStQf0ka5Rh15BA5m7cCCH4jmHqkw==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-3.4.0.tgz", + "integrity": "sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g==", "dev": true, "requires": { "cross-spawn": "^7.0.0", @@ -12777,6 +12790,15 @@ "escape-string-regexp": "^1.0.5" } }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, "find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -12805,6 +12827,18 @@ "lru-cache": "^5.1.1" } }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, "is-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", @@ -12829,6 +12863,16 @@ "yallist": "^3.0.2" } }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, "mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -12836,9 +12880,9 @@ "dev": true }, "npm-run-path": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.0.tgz", - "integrity": "sha512-8eyAOAH+bYXFPSnNnKr3J+yoybe8O87Is5rtAQ8qRczJz1ajcsjg8l2oZqP+Ppx15Ii3S1vUTjQN2h4YO2tWWQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "requires": { "path-key": "^3.0.0" @@ -12887,9 +12931,9 @@ "dev": true }, "path-key": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.0.tgz", - "integrity": "sha512-8cChqz0RP6SHJkMt48FW0A7+qUOn+OsnOsVtzI59tZ8m+5bCSk7hzwET0pulwOM2YMn9J1efb07KB9l9f30SGg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "read-pkg": { @@ -12913,9 +12957,9 @@ } }, "read-pkg-up": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.0.tgz", - "integrity": "sha512-t2ODkS/vTTcRlKwZiZsaLGb5iwfx9Urp924aGzVyboU6+7Z2i6eGr/G1Z4mjvwLLQV3uFOBKobNRGM3ux2PD/w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", "dev": true, "requires": { "find-up": "^4.1.0", @@ -12935,6 +12979,15 @@ "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "requires": { + "semver": "^6.3.0" + } + }, "shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -12950,6 +13003,35 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -12957,14 +13039,25 @@ "dev": true }, "which": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.1.tgz", - "integrity": "sha512-N7GBZOTswtB9lkQBZA4+zAXrjEIWAUOB93AvzUiudRzRxhUdLURQ7D/gAIMY1gatT/LTbmbcv8SiYazy3eYB7w==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" } }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, "yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", @@ -12972,64 +13065,28 @@ "dev": true }, "yargs": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.0.tgz", - "integrity": "sha512-/is78VKbKs70bVZH7w4YaZea6xcJWOAwkhbR0CFuZBmYtfTYF0xjGJF43AYd8g2Uii1yJwmS5GR2vBmrc32sbg==", + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.1.0.tgz", + "integrity": "sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg==", "dev": true, "requires": { - "cliui": "^5.0.0", + "cliui": "^6.0.0", "decamelize": "^1.2.0", - "find-up": "^3.0.0", + "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", - "string-width": "^3.0.0", + "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", - "yargs-parser": "^15.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } + "yargs-parser": "^16.1.0" } }, "yargs-parser": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.0.tgz", - "integrity": "sha512-xLTUnCMc4JhxrPEPUYD5IBR1mWCK/aT6+RJ/K29JY2y1vD+FhtgKK0AXRWvI262q3QSffAQuTouFIKUuHX89wQ==", + "version": "16.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-16.1.0.tgz", + "integrity": "sha512-H/V41UNZQPkUMIT5h5hiwg4QKIY1RPvoBV4XcjUbRM8Bk2oKqqyZ0DIEbTFZB0XjbtSPG8SAa/0DxCQmiRgzKg==", "dev": true, "requires": { "camelcase": "^5.0.0", @@ -13205,6 +13262,12 @@ "supports-color": "^5.5.0" } }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, "slice-ansi": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", @@ -13716,9 +13779,9 @@ }, "dependencies": { "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "requires": { "core-util-is": "~1.0.0", @@ -13983,9 +14046,9 @@ } }, "text-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.0.0.tgz", - "integrity": "sha512-F91ZqLgvi1E0PdvmxMgp+gcf6q8fMH7mhdwWfzXnl1k+GbpQDmi8l7DzLC5JTASKbwpY3TfxajAUzAXcv2NmsQ==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", "dev": true }, "text-hex": { @@ -14899,6 +14962,15 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" }, + "yaml": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.7.2.tgz", + "integrity": "sha512-qXROVp90sb83XtAoqE8bP9RwAkTTZbugRUTm5YeFCBfNRPEp2YzTeqWiz7m5OORHzEvrA/qcGS8hp/E+MMROYw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.6.3" + } + }, "yargs": { "version": "13.3.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", From cd64b26d5fa30e405b177dc3b6846a280a991898 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sat, 11 Jan 2020 11:35:49 -0800 Subject: [PATCH 49/49] Removed nodemon --- package-lock.json | 1182 --------------------------------------------- package.json | 1 - 2 files changed, 1183 deletions(-) diff --git a/package-lock.json b/package-lock.json index 430ba6e..0aaba93 100644 --- a/package-lock.json +++ b/package-lock.json @@ -884,42 +884,6 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-2.1.1.tgz", "integrity": "sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I=" }, - "ansi-align": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", - "dev": true, - "requires": { - "string-width": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "ansi-colors": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", @@ -949,27 +913,6 @@ "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=", "dev": true }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, "apidoc": { "version": "0.17.7", "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.17.7.tgz", @@ -1185,12 +1128,6 @@ "lodash": "^4.17.14" } }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true - }, "async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", @@ -1495,12 +1432,6 @@ "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz", "integrity": "sha512-t/OYhhJ2SD+YGBQcjY8GzzDHEk9f3nerxjtfa6tlMXfe7frs/WozhvCNoGvpM0P3bNf3Gq5ZRMlGr5f3r4/N8A==" }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true - }, "bindings": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", @@ -1724,54 +1655,6 @@ "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.7.0.tgz", "integrity": "sha512-aIlMvstvu8x+34KEiOHD3AsBgdrzg6sxALYiukOWhFvGMbQI6TRP/iY0LMhUrHs56aD6P1G0Z7h45PUJaa5m9w==" }, - "boxen": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", - "dev": true, - "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2022,12 +1905,6 @@ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" }, - "capture-stack-trace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", - "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", - "dev": true - }, "cardinal": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/cardinal/-/cardinal-2.1.1.tgz", @@ -2094,43 +1971,6 @@ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "dev": true }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - }, - "dependencies": { - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - } - } - }, - "ci-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", - "dev": true - }, "cipher-base": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", @@ -2178,12 +2018,6 @@ "resolved": "https://registry.npmjs.org/clear/-/clear-0.1.0.tgz", "integrity": "sha512-qMjRnoL+JDPJHeLePZJuao6+8orzHMGP04A8CdwCNsKhRbOnKRjefxONR7bwILT3MHecxKBjHkKL/tkZ8r4Uzw==" }, - "cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", - "dev": true - }, "cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", @@ -2412,37 +2246,6 @@ } } }, - "configstore": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", - "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", - "dev": true, - "requires": { - "dot-prop": "^4.1.0", - "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" - }, - "dependencies": { - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, "contains-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", @@ -2740,15 +2543,6 @@ } } }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "dev": true, - "requires": { - "capture-stack-trace": "^1.0.0" - } - }, "create-hash": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", @@ -3091,15 +2885,6 @@ "resolved": "https://registry.npmjs.org/dont-sniff-mimetype/-/dont-sniff-mimetype-1.1.0.tgz", "integrity": "sha512-ZjI4zqTaxveH2/tTlzS1wFp+7ncxNZaIEWYg3lzZRHkKf5zPT/MnEG6WL0BhHMJUabkh8GeU5NL5j+rEUCb7Ug==" }, - "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", - "dev": true, - "requires": { - "is-obj": "^1.0.0" - } - }, "dotenv": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", @@ -3156,12 +2941,6 @@ } } }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -4276,34 +4055,6 @@ "safe-buffer": "^5.1.1" } }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - } - } - }, "expand-brackets": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", @@ -5007,554 +4758,6 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "fsevents": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", - "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "debug": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "needle": { - "version": "2.3.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "^4.1.0", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.12.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "dev": true, - "optional": true - }, - "semver": { - "version": "5.7.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "dev": true, - "optional": true - } - } - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -5581,12 +4784,6 @@ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==" }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true - }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -5700,15 +4897,6 @@ } } }, - "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", - "dev": true, - "requires": { - "ini": "^1.3.4" - } - }, "globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", @@ -5738,25 +4926,6 @@ } } }, - "got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", - "dev": true, - "requires": { - "create-error-class": "^3.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" - } - }, "graceful-fs": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", @@ -6150,12 +5319,6 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.10.tgz", "integrity": "sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug==" }, - "ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", - "dev": true - }, "immediate": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.2.3.tgz", @@ -6188,12 +5351,6 @@ } } }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", - "dev": true - }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -6334,15 +5491,6 @@ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, "is-buffer": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", @@ -6353,15 +5501,6 @@ "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==", - "dev": true, - "requires": { - "ci-info": "^1.5.0" - } - }, "is-data-descriptor": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", @@ -6441,16 +5580,6 @@ "is-extglob": "^2.1.0" } }, - "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", - "dev": true, - "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" - } - }, "is-nan": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.2.1.tgz", @@ -6464,12 +5593,6 @@ "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=" }, - "is-npm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", - "dev": true - }, "is-number": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", @@ -6508,15 +5631,6 @@ "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=", "dev": true }, - "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, "is-plain-obj": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", @@ -6537,12 +5651,6 @@ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=" }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", - "dev": true - }, "is-regex": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", @@ -6564,12 +5672,6 @@ "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.1.0.tgz", "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==" }, - "is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", - "dev": true - }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -6906,15 +6008,6 @@ "colornames": "^1.1.1" } }, - "latest-version": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", - "dev": true, - "requires": { - "package-json": "^4.0.0" - } - }, "lcov-parse": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/lcov-parse/-/lcov-parse-0.0.10.tgz", @@ -7187,12 +6280,6 @@ "signal-exit": "^3.0.0" } }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, "lru-cache": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", @@ -7898,41 +6985,6 @@ "process-on-spawn": "^1.0.0" } }, - "nodemon": { - "version": "1.19.4", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-1.19.4.tgz", - "integrity": "sha512-VGPaqQBNk193lrJFotBU8nvWZPqEZY2eIzymy2jjY0fJ9qIsxA0sxQ8ATPl0gZC645gijYEc1jtZvpS8QWzJGQ==", - "dev": true, - "requires": { - "chokidar": "^2.1.8", - "debug": "^3.2.6", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.7", - "semver": "^5.7.1", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.2", - "update-notifier": "^2.5.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, "nopt": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", @@ -7952,12 +7004,6 @@ "validate-npm-package-license": "^3.0.1" } }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, "normalize-url": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", @@ -12004,18 +11050,6 @@ "release-zalgo": "^1.0.0" } }, - "package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", - "dev": true, - "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" - } - }, "packet-reader": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", @@ -12399,12 +11433,6 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, "prettier": { "version": "1.18.2", "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz", @@ -12490,12 +11518,6 @@ "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==", "dev": true }, - "pstree.remy": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz", - "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==", - "dev": true - }, "pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", @@ -12655,49 +11677,6 @@ "util-deprecate": "^1.0.1" } }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, "redent": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz", @@ -12744,25 +11723,6 @@ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, - "registry-auth-token": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", - "dev": true, - "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" - } - }, - "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", - "dev": true, - "requires": { - "rc": "^1.0.1" - } - }, "reinterval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz", @@ -13476,15 +12436,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" }, - "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", - "dev": true, - "requires": { - "semver": "^5.0.3" - } - }, "semver-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-2.0.0.tgz", @@ -14434,15 +13385,6 @@ } } }, - "term-size": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", - "dev": true, - "requires": { - "execa": "^0.7.0" - } - }, "test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -14521,12 +13463,6 @@ "xtend": "~4.0.0" } }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true - }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -14777,32 +13713,6 @@ "resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz", "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=" }, - "undefsafe": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz", - "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=", - "dev": true, - "requires": { - "debug": "^2.2.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, "underscore": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", @@ -14902,36 +13812,6 @@ } } }, - "unzip-response": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", - "dev": true - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true - }, - "update-notifier": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", - "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", - "dev": true, - "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -14953,15 +13833,6 @@ "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "dev": true }, - "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", - "dev": true, - "requires": { - "prepend-http": "^1.0.1" - } - }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -15141,42 +14012,6 @@ } } }, - "widest-line": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", - "dev": true, - "requires": { - "string-width": "^2.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "wif": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/wif/-/wif-2.0.6.tgz", @@ -15333,17 +14168,6 @@ "mkdirp": "^0.5.1" } }, - "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" - } - }, "ws": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/ws/-/ws-7.2.0.tgz", @@ -15357,12 +14181,6 @@ "resolved": "https://registry.npmjs.org/x-xss-protection/-/x-xss-protection-1.3.0.tgz", "integrity": "sha512-kpyBI9TlVipZO4diReZMAHWtS0MMa/7Kgx8hwG/EuZLiA6sg4Ah/4TRdASHhRRN3boobzcYgFRUFSgHRge6Qhg==" }, - "xdg-basedir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", - "dev": true - }, "xmlhttprequest-ssl": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz", diff --git a/package.json b/package.json index f1493d8..b566ce2 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,6 @@ "fs-extra": "^8.0.1", "nock": "^11.1.0", "node-mocks-http": "^1.7.0", - "nodemon": "^1.18.1", "nyc": "^15.0.0", "prettier": "^1.18.2", "proxyquire": "^2.1.0",