From 376094c6c210f7b94cb474c00b4af687bf8b97c9 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 27 Sep 2020 00:21:36 -0700 Subject: [PATCH 01/22] feat(hydrateUtxos): Porting hydrateUtxos convenience utility function --- package-lock.json | 284 ++++++++++++++++++------------------- package.json | 2 +- src/routes/v3/slp.js | 70 +++++++++ test/v3/integration/slp.js | 64 ++++++++- test/v3/slp.js | 142 +++++++++++++++++++ 5 files changed, 411 insertions(+), 151 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9b2c98f..766a8f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -147,106 +147,6 @@ "to-fast-properties": "^2.0.0" } }, - "@chris.troutner/bch-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@chris.troutner/bch-js/-/bch-js-3.0.1.tgz", - "integrity": "sha512-XTOCFeMCBThXlGxdWsmW6OmfZBqa3XtsvFGE3hIapWfvoCUd6fSyegn9G1mXod236D3qKTW/Vk/NBwGlc7Y1YQ==", - "requires": { - "@uppy/core": "^1.10.4", - "@uppy/tus": "^1.5.12", - "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", - "bip38": "^2.0.2", - "bip39": "^3.0.2", - "bip66": "^1.1.5", - "bitcoincash-ops": "github:christroutner/bitcoincash-ops", - "bitcoincashjs-lib": "github:christroutner/bitcoincashjs-lib", - "bitcoinjs-message": "^2.0.0", - "bs58": "^4.0.1", - "buffer": "^5.1.0", - "cashaddrjs": "^0.3.3", - "chalk": "^2.3.0", - "clear": "0.1.0", - "coininfo": "github:Bitcoin-com/coininfo", - "commander": "^3.0.0", - "cp-file": "^7.0.0", - "ecurve": "^1.0.6", - "figlet": "^1.2.0", - "git-clone": "^0.1.0", - "ini": "^1.3.5", - "mkdirp": "^0.5.1", - "node-cmd": "^3.0.0", - "node-emoji": "^1.8.1", - "qrcode": "^1.4.1", - "randombytes": "^2.0.6", - "repl.history": "^0.1.4", - "safe-buffer": "^5.1.2", - "satoshi-bitcoin": "^1.0.4", - "slp-mdm": "0.0.6", - "slp-parser": "0.0.4", - "socket.io": "^2.1.1", - "socket.io-client": "^2.1.1", - "touch": "^3.1.0", - "wif": "^2.0.6" - }, - "dependencies": { - "apidoc": { - "version": "0.17.7", - "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.17.7.tgz", - "integrity": "sha512-9Wf4bRPwCuWOIOxR42dDnsXnFw+rhJg5VrMQK+KmNxJwyIh30UqX6gvjjXSG6YO74MqE87F18bbQXUENK9dPGg==", - "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" - }, - "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" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - } - } - }, "@istanbuljs/load-nyc-config": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz", @@ -465,6 +365,127 @@ "@types/node": ">= 8" } }, + "@psf/bch-js": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-3.5.5.tgz", + "integrity": "sha512-bZs1p6VNDBYJPJuKuYD4h2Wsp+gt38181XlVX+2Yq8JGFOvf5ZRSrkqcd0LYvfiP8f9IA46aaAtTaLQT1KQsJw==", + "requires": { + "@uppy/core": "^1.10.4", + "@uppy/tus": "^1.5.12", + "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", + "bip38": "^2.0.2", + "bip39": "^3.0.2", + "bip66": "^1.1.5", + "bitcoincash-ops": "github:christroutner/bitcoincash-ops", + "bitcoincashjs-lib": "github:christroutner/bitcoincashjs-lib#master", + "bitcoinjs-message": "^2.0.0", + "bs58": "^4.0.1", + "buffer": "^5.1.0", + "cashaddrjs": "^0.3.3", + "chalk": "^2.3.0", + "clear": "0.1.0", + "coininfo": "github:Bitcoin-com/coininfo", + "commander": "^3.0.0", + "cp-file": "^7.0.0", + "ecurve": "^1.0.6", + "figlet": "^1.2.0", + "git-clone": "^0.1.0", + "ini": "^1.3.5", + "mkdirp": "^0.5.1", + "node-cmd": "^3.0.0", + "node-emoji": "^1.8.1", + "qrcode": "^1.4.1", + "randombytes": "^2.0.6", + "repl.history": "^0.1.4", + "safe-buffer": "^5.1.2", + "satoshi-bitcoin": "^1.0.4", + "slp-mdm": "0.0.6", + "slp-parser": "0.0.4", + "socket.io": "^2.1.1", + "socket.io-client": "^2.1.1", + "touch": "^3.1.0", + "wif": "^2.0.6" + }, + "dependencies": { + "apidoc": { + "version": "0.17.7", + "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.17.7.tgz", + "integrity": "sha512-9Wf4bRPwCuWOIOxR42dDnsXnFw+rhJg5VrMQK+KmNxJwyIh30UqX6gvjjXSG6YO74MqE87F18bbQXUENK9dPGg==", + "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" + }, + "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==" + } + } + }, + "bitcoincashjs-lib": { + "version": "github:christroutner/bitcoincashjs-lib#715823b5d6eb3e2961e715605ad1464f3223da0b", + "from": "github:christroutner/bitcoincashjs-lib#master", + "requires": { + "bech32": "^1.1.2", + "bigi": "^1.4.0", + "bip66": "^1.1.0", + "bitcoincash-ops": "github:christroutner/bitcoincash-ops", + "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.18.0", + "varuint-bitcoin": "^1.0.4", + "wif": "^2.0.1" + } + }, + "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" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + } + } + }, "@semantic-release/commit-analyzer": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@semantic-release/commit-analyzer/-/commit-analyzer-7.0.0.tgz", @@ -2289,27 +2310,6 @@ "resolved": "https://registry.npmjs.org/bitcoincashjs/-/bitcoincashjs-0.1.14.tgz", "integrity": "sha512-x0wMVSCZ56ZQnSQ+Xim7XfS0IYiOyOwo0pdMCmR+cDNB7zRhICFuH5i3lNdLSgUw1e7Pc3FmUlC9wruQNlYMnA==" }, - "bitcoincashjs-lib": { - "version": "github:christroutner/bitcoincashjs-lib#b03ca974d4fd548cb42c7a522863e5d9be6051ef", - "from": "github:christroutner/bitcoincashjs-lib", - "requires": { - "bech32": "^1.1.2", - "bigi": "^1.4.0", - "bip66": "^1.1.0", - "bitcoincash-ops": "github:christroutner/bitcoincash-ops", - "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.18.0", - "varuint-bitcoin": "^1.0.4", - "wif": "^2.0.1" - } - }, "bitcoinjs-message": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/bitcoinjs-message/-/bitcoinjs-message-2.1.0.tgz", @@ -2324,42 +2324,28 @@ } }, "bitcore-lib": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/bitcore-lib/-/bitcore-lib-8.21.0.tgz", - "integrity": "sha512-Z1lDqSReD/Ah/n7qEsR564AlNImwYeB1MQ0fnOD6e2piNsTXQ3cSjgH93MxVARpX4LRkEev2XBnYCJ4NcYTQdQ==", + "version": "8.22.2", + "resolved": "https://registry.npmjs.org/bitcore-lib/-/bitcore-lib-8.22.2.tgz", + "integrity": "sha512-atl/RN7x2R/JQMaIgUzX0EQZ+I/d7fTaOoS2/5k+H5POGc2vygWZ9MWpGJi3T9Yb5jM/cT5aFsrDq8s8l6lqgQ==", "requires": { "bech32": "=1.1.3", "bn.js": "=4.11.8", "bs58": "^4.0.1", "buffer-compare": "=1.1.1", - "elliptic": "=6.4.0", + "elliptic": "^6.5.3", "inherits": "=2.0.1", - "lodash": "=4.17.15" + "lodash": "^4.17.20" }, "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.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" } } }, @@ -2404,9 +2390,9 @@ } }, "bl": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz", - "integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -9081,9 +9067,9 @@ } }, "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==", "dev": true }, "node-gyp-build": { diff --git a/package.json b/package.json index aa87993..f63a082 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "node": ">=10.15.1" }, "dependencies": { - "@chris.troutner/bch-js": "^3.0.1", + "@psf/bch-js": "^3.5.5", "apidoc": "^0.23.0", "axios": "^0.19.0", "bitcore-lib-cash": "^8.20.3", diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index 4b85bbb..b3742f1 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -79,6 +79,7 @@ class Slp { _this.txsByAddressSingle ) _this.router.post('/generateSendOpReturn', _this.generateSendOpReturn) + _this.router.post('/hydrateUtxos', _this.hydrateUtxos) } // DRY error handler. @@ -1519,6 +1520,75 @@ class Slp { }) } } + + /** + * @api {post} /slp/hydrateUtxos/ hydrateUtxos + * @apiName SLP hydrateUtxos + * @apiGroup SLP + * @apiDescription Hydrate UTXO data with SLP information. + * + * Expects an array of UTXO objects as input. Returns an array of equal size. + * Returns UTXO data hydrated with token information. If the UTXO does not + * belong to a SLP transaction, it will return an isValid property set to + * false. If the UTXO is part of an SLP transaction, it will return the UTXO + * object with additional SLP information attached. An isValid property will + * be included. If its value is true, the UTXO is a valid SLP UTXO. If the + * value is null, then SLPDB has not yet processed that txid and validity has + * not been confirmed. + * + * @apiExample Example usage: + * curl -X POST "https://api.fullstack.cash/v3/slp/hydrateUtxos" -H "accept:application/json" -H "Content-Type: application/json" -d '{"utxos":[{"txid": "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56","vout": 3, "value": "6816", "height": 606848, "confirmations": 13, "satoshis": 6816}, {"txid": "d56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56","vout": 2, "value": "546", "height": 606848, "confirmations": 13, "satoshis": 546}]}' + * + * + */ + async hydrateUtxos (req, res, next) { + try { + const utxos = req.body.utxos + + // Validate inputs + if (!Array.isArray(utxos)) { + res.status(422) + return res.json({ + error: 'Input must be an array.' + }) + } + + if (!utxos.length) { + res.status(422) + return res.json({ + error: 'Array should not be empty' + }) + } + + if (utxos.length > 20) { + res.status(422) + return res.json({ + error: 'Array too long, max length is 20' + }) + } + + // Get Token Utxo Details + const details = await _this.bchjs.SLP.Utils.tokenUtxoDetails(utxos) + // console.log('details : ', details) + + res.status(200) + return res.json({ slpUtxos: details }) + } catch (err) { + wlogger.error('Error in slp.js/hydrateUtxos().', 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 hydrateUtxos()' + }) + } + } } module.exports = Slp diff --git a/test/v3/integration/slp.js b/test/v3/integration/slp.js index 2ce5bbe..9f41c70 100644 --- a/test/v3/integration/slp.js +++ b/test/v3/integration/slp.js @@ -102,7 +102,7 @@ describe('#slp', () => { }) }) - describe('generateSendOpReturn', () => { + describe('#generateSendOpReturn', () => { it('should return OP_RETURN script', async () => { req.body.tokenUtxos = [ { @@ -121,4 +121,66 @@ describe('#slp', () => { assert.isNumber(result.outputs) }) }) + + describe('#hydrateUtxos', () => { + it('should return utxo details', async () => { + const utxos = [ + { + txid: + 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', + vout: 3, + value: '6816', + height: 606848, + confirmations: 13, + satoshis: 6816 + }, + { + txid: + 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', + vout: 2, + value: '546', + height: 606848, + confirmations: 13, + satoshis: 546 + } + ] + + req.body.utxos = utxos + const result = await slp.hydrateUtxos(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + // Test the general structure of the output. + assert.isArray(result.slpUtxos) + assert.equal(result.slpUtxos.length, 2) + + // Test the non-slp UTXO. + assert.property(result.slpUtxos[0], 'txid') + assert.property(result.slpUtxos[0], 'vout') + assert.property(result.slpUtxos[0], 'value') + assert.property(result.slpUtxos[0], 'height') + assert.property(result.slpUtxos[0], 'confirmations') + assert.property(result.slpUtxos[0], 'satoshis') + assert.property(result.slpUtxos[0], 'isValid') + assert.equal(result.slpUtxos[0].isValid, false) + + // Test the slp UTXO. + assert.property(result.slpUtxos[1], 'txid') + assert.property(result.slpUtxos[1], 'vout') + assert.property(result.slpUtxos[1], 'value') + assert.property(result.slpUtxos[1], 'height') + assert.property(result.slpUtxos[1], 'confirmations') + assert.property(result.slpUtxos[1], 'satoshis') + assert.property(result.slpUtxos[1], 'isValid') + assert.equal(result.slpUtxos[1].isValid, true) + assert.property(result.slpUtxos[1], 'transactionType') + assert.property(result.slpUtxos[1], 'tokenId') + assert.property(result.slpUtxos[1], 'tokenTicker') + assert.property(result.slpUtxos[1], 'tokenName') + assert.property(result.slpUtxos[1], 'tokenDocumentUrl') + assert.property(result.slpUtxos[1], 'tokenDocumentHash') + assert.property(result.slpUtxos[1], 'decimals') + assert.property(result.slpUtxos[1], 'tokenType') + assert.property(result.slpUtxos[1], 'tokenQty') + }) + }) }) diff --git a/test/v3/slp.js b/test/v3/slp.js index 89e1dbc..34a4d36 100644 --- a/test/v3/slp.js +++ b/test/v3/slp.js @@ -888,6 +888,148 @@ describe('#SLP', () => { assert.isNumber(result.outputs) }) }) + + describe('#hydrateUtxos', () => { + it('should throw error if input is not an array.', async () => { + req.body.utxos = 'test' + + const result = await slpRoute.hydrateUtxos(req, res) + // console.log(`result: `, result) + + assert.hasAllKeys(result, ['error']) + assert.include(result.error, 'Input must be an array') + }) + + it('should throw error if Array is empty', async () => { + req.body.utxos = [] + + const result = await slpRoute.hydrateUtxos(req, res) + + assert.hasAllKeys(result, ['error']) + assert.include(result.error, 'Array should not be empty') + }) + + it('should throw error if Array is too long', async () => { + const utxo = { + txid: + 'bd158c564dd4ef54305b14f44f8e94c44b649f246dab14bcb42fb0d0078b8a90', + vout: 3, + amount: 0.00002015, + satoshis: 2015, + height: 594892, + confirmations: 5 + } + + const utxos = [] + + // Populate array with 21 utxos + for (let i = 0; i < 21; i++) { + utxos.push(utxo) + } + + req.body.utxos = utxos + + const result = await slpRoute.hydrateUtxos(req, res) + + assert.hasAllKeys(result, ['error']) + assert.include(result.error, 'Array too long, max length is 20') + }) + + it('should return utxo details', async () => { + const utxos = [ + { + txid: + 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', + vout: 3, + value: '6816', + height: 606848, + confirmations: 13, + satoshis: 6816 + }, + { + txid: + 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', + vout: 2, + value: '546', + height: 606848, + confirmations: 13, + satoshis: 546 + } + ] + + // Mock the external network call. + sandbox.stub(slpRoute.bchjs.SLP.Utils, 'tokenUtxoDetails').resolves([ + { + txid: + 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', + vout: 3, + value: '6816', + height: 606848, + confirmations: 13, + satoshis: 6816, + isValid: false + }, + { + txid: + 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', + vout: 2, + value: '546', + height: 606848, + confirmations: 13, + satoshis: 546, + utxoType: 'token', + transactionType: 'send', + tokenId: + 'dd84ca78db4d617221b58eabc6667af8fe2f7eadbfcc213d35be9f1b419beb8d', + tokenTicker: 'TAP', + tokenName: 'Thoughts and Prayers', + tokenDocumentUrl: '', + tokenDocumentHash: '', + decimals: 0, + tokenType: 1, + tokenQty: 5, + isValid: true + } + ]) + + req.body.utxos = utxos + const result = await slpRoute.hydrateUtxos(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + // Test the general structure of the output. + assert.isArray(result.slpUtxos) + assert.equal(result.slpUtxos.length, 2) + + // Test the non-slp UTXO. + assert.property(result.slpUtxos[0], 'txid') + assert.property(result.slpUtxos[0], 'vout') + assert.property(result.slpUtxos[0], 'value') + assert.property(result.slpUtxos[0], 'height') + assert.property(result.slpUtxos[0], 'confirmations') + assert.property(result.slpUtxos[0], 'satoshis') + assert.property(result.slpUtxos[0], 'isValid') + assert.equal(result.slpUtxos[0].isValid, false) + + // Test the slp UTXO. + assert.property(result.slpUtxos[1], 'txid') + assert.property(result.slpUtxos[1], 'vout') + assert.property(result.slpUtxos[1], 'value') + assert.property(result.slpUtxos[1], 'height') + assert.property(result.slpUtxos[1], 'confirmations') + assert.property(result.slpUtxos[1], 'satoshis') + assert.property(result.slpUtxos[1], 'isValid') + assert.equal(result.slpUtxos[1].isValid, true) + assert.property(result.slpUtxos[1], 'transactionType') + assert.property(result.slpUtxos[1], 'tokenId') + assert.property(result.slpUtxos[1], 'tokenTicker') + assert.property(result.slpUtxos[1], 'tokenName') + assert.property(result.slpUtxos[1], 'tokenDocumentUrl') + assert.property(result.slpUtxos[1], 'tokenDocumentHash') + assert.property(result.slpUtxos[1], 'decimals') + assert.property(result.slpUtxos[1], 'tokenType') + assert.property(result.slpUtxos[1], 'tokenQty') + }) + }) }) /* From cb416d26c0c31fd6e087c4f4758bdc7e0c66ab0b Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 27 Sep 2020 00:26:57 -0700 Subject: [PATCH 02/22] Replacing @chris.troutner/bch-js with @psf/bch-js --- src/routes/v3/blockbook.js | 2 +- src/routes/v3/electrumx.js | 2 +- src/routes/v3/encryption.js | 2 +- src/routes/v3/full-node/blockchain.js | 2 +- src/routes/v3/ninsight.js | 2 +- src/routes/v3/route-utils.js | 2 +- src/routes/v3/services/slpdb.js | 2 +- src/routes/v3/slp.js | 2 +- src/routes/v3/util.js | 2 +- src/routes/v3/xpub.js | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/routes/v3/blockbook.js b/src/routes/v3/blockbook.js index 7b5bd07..aafd7a6 100644 --- a/src/routes/v3/blockbook.js +++ b/src/routes/v3/blockbook.js @@ -23,7 +23,7 @@ const router = express.Router() const util = require('util') util.inspect.defaultOptions = { depth: 1 } -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() let _this diff --git a/src/routes/v3/electrumx.js b/src/routes/v3/electrumx.js index 5dbb4d0..8e30e7b 100644 --- a/src/routes/v3/electrumx.js +++ b/src/routes/v3/electrumx.js @@ -19,7 +19,7 @@ const config = require('../../../config') const RouteUtils = require('../../util/route-utils') const routeUtils = new RouteUtils() -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() let _this diff --git a/src/routes/v3/encryption.js b/src/routes/v3/encryption.js index d6285f4..859484a 100644 --- a/src/routes/v3/encryption.js +++ b/src/routes/v3/encryption.js @@ -15,7 +15,7 @@ const config = require('../../../config') const RouteUtils = require('../../util/route-utils') const routeUtils = new RouteUtils() -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() // Use Blockbook for getting indexer data. diff --git a/src/routes/v3/full-node/blockchain.js b/src/routes/v3/full-node/blockchain.js index 94a5306..b021a48 100644 --- a/src/routes/v3/full-node/blockchain.js +++ b/src/routes/v3/full-node/blockchain.js @@ -17,7 +17,7 @@ const routeUtils = new RouteUtils() const util = require('util') util.inspect.defaultOptions = { depth: 1 } -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() let _this diff --git a/src/routes/v3/ninsight.js b/src/routes/v3/ninsight.js index 1a40e66..614be72 100644 --- a/src/routes/v3/ninsight.js +++ b/src/routes/v3/ninsight.js @@ -11,7 +11,7 @@ const express = require('express') const router = express.Router() -// const BCHJS = require('@chris.troutner/bch-js') +// const BCHJS = require('@psf/bch-js') // const bchjs = new BCHJS() // let _this diff --git a/src/routes/v3/route-utils.js b/src/routes/v3/route-utils.js index 1a4cba1..fe86c44 100644 --- a/src/routes/v3/route-utils.js +++ b/src/routes/v3/route-utils.js @@ -10,7 +10,7 @@ const wlogger = require('../../util/winston-logging') const util = require('util') util.inspect.defaultOptions = { depth: 1 } -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() module.exports = { diff --git a/src/routes/v3/services/slpdb.js b/src/routes/v3/services/slpdb.js index 9690e4e..c19c52e 100644 --- a/src/routes/v3/services/slpdb.js +++ b/src/routes/v3/services/slpdb.js @@ -1,6 +1,6 @@ const axios = require('axios') -const SLPSDK = require('@chris.troutner/bch-js') +const SLPSDK = require('@psf/bch-js') const SLP = new SLPSDK() class Slpdb { diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index b3742f1..43e3b42 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -13,7 +13,7 @@ const Slpdb = require('./services/slpdb') // const strftime = require('strftime') const wlogger = require('../../util/winston-logging') -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() // Used to convert error messages to strings, to safely pass to users. diff --git a/src/routes/v3/util.js b/src/routes/v3/util.js index 77f028b..13c663a 100644 --- a/src/routes/v3/util.js +++ b/src/routes/v3/util.js @@ -12,7 +12,7 @@ const blockbook = new Blockbook() const util = require('util') util.inspect.defaultOptions = { depth: 1 } -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() // const BCHJS_TESTNET = 'https://testnet.bchjs.cash/v3/' diff --git a/src/routes/v3/xpub.js b/src/routes/v3/xpub.js index 353158f..fa1d9d4 100644 --- a/src/routes/v3/xpub.js +++ b/src/routes/v3/xpub.js @@ -16,7 +16,7 @@ const router = express.Router() const util = require('util') util.inspect.defaultOptions = { depth: 1 } -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() // Connect the route endpoints to their handler functions. From 6f25ec4b681795c0ebf63dd35eaaf0a6bf4bf2c8 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 27 Sep 2020 00:31:38 -0700 Subject: [PATCH 03/22] Replacing @chris.troutner/bch-js with @psf/bch-js --- src/util/route-utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/route-utils.js b/src/util/route-utils.js index 1636cba..a12abd4 100644 --- a/src/util/route-utils.js +++ b/src/util/route-utils.js @@ -10,7 +10,7 @@ const wlogger = require('./winston-logging') const util = require('util') util.inspect.defaultOptions = { depth: 1 } -const BCHJS = require('@chris.troutner/bch-js') +const BCHJS = require('@psf/bch-js') const bchjs = new BCHJS() // let _this From 0a14ca08a4947123c8c20e503ab47c54aa01ab96 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Sun, 27 Sep 2020 17:26:34 -0700 Subject: [PATCH 04/22] fix(hydrateUtxos): Adjusting input to match output of Electrumx.utxo() --- src/routes/v3/slp.js | 16 ++++-- test/v3/integration/slp.js | 114 +++++++++++++++++++++++-------------- test/v3/slp.js | 89 +++++++++++++++-------------- 3 files changed, 131 insertions(+), 88 deletions(-) diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index 43e3b42..3a7ef33 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -1567,12 +1567,20 @@ class Slp { }) } - // Get Token Utxo Details - const details = await _this.bchjs.SLP.Utils.tokenUtxoDetails(utxos) - // console.log('details : ', details) + // Loop through each address and query the UTXOs for that element. + for (let i = 0; i < utxos.length; i++) { + const theseUtxos = utxos[i].utxos + + // Get SLP token details. + const details = await _this.bchjs.SLP.Utils.tokenUtxoDetails(theseUtxos) + // console.log('details : ', details) + + // Replace the original UTXO data with the hydrated data. + utxos[i].utxos = details + } res.status(200) - return res.json({ slpUtxos: details }) + return res.json({ slpUtxos: utxos }) } catch (err) { wlogger.error('Error in slp.js/hydrateUtxos().', err) diff --git a/test/v3/integration/slp.js b/test/v3/integration/slp.js index 9f41c70..279d077 100644 --- a/test/v3/integration/slp.js +++ b/test/v3/integration/slp.js @@ -22,6 +22,9 @@ if (!process.env.SLPDB_URL) { const SLP = require('../../../src/routes/v3/slp') const slp = new SLP() +const BCHJS = require('@psf/bch-js') +const bchjs = new BCHJS() + const { mockReq, mockRes } = require('../mocks/express-mocks') describe('#slp', () => { @@ -126,22 +129,26 @@ describe('#slp', () => { it('should return utxo details', async () => { const utxos = [ { - txid: - 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', - vout: 3, - value: '6816', - height: 606848, - confirmations: 13, - satoshis: 6816 - }, - { - txid: - 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', - vout: 2, - value: '546', - height: 606848, - confirmations: 13, - satoshis: 546 + utxos: [ + { + txid: + 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', + vout: 3, + value: '6816', + height: 606848, + confirmations: 13, + satoshis: 6816 + }, + { + txid: + 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', + vout: 2, + value: '546', + height: 606848, + confirmations: 13, + satoshis: 546 + } + ] } ] @@ -151,36 +158,59 @@ describe('#slp', () => { // Test the general structure of the output. assert.isArray(result.slpUtxos) - assert.equal(result.slpUtxos.length, 2) + assert.equal(result.slpUtxos.length, 1) + assert.equal(result.slpUtxos[0].utxos.length, 2) // Test the non-slp UTXO. - assert.property(result.slpUtxos[0], 'txid') - assert.property(result.slpUtxos[0], 'vout') - assert.property(result.slpUtxos[0], 'value') - assert.property(result.slpUtxos[0], 'height') - assert.property(result.slpUtxos[0], 'confirmations') - assert.property(result.slpUtxos[0], 'satoshis') - assert.property(result.slpUtxos[0], 'isValid') - assert.equal(result.slpUtxos[0].isValid, false) + assert.property(result.slpUtxos[0].utxos[0], 'txid') + assert.property(result.slpUtxos[0].utxos[0], 'vout') + assert.property(result.slpUtxos[0].utxos[0], 'value') + assert.property(result.slpUtxos[0].utxos[0], 'height') + assert.property(result.slpUtxos[0].utxos[0], 'confirmations') + assert.property(result.slpUtxos[0].utxos[0], 'satoshis') + assert.property(result.slpUtxos[0].utxos[0], 'isValid') + assert.equal(result.slpUtxos[0].utxos[0].isValid, false) // Test the slp UTXO. - assert.property(result.slpUtxos[1], 'txid') - assert.property(result.slpUtxos[1], 'vout') - assert.property(result.slpUtxos[1], 'value') - assert.property(result.slpUtxos[1], 'height') - assert.property(result.slpUtxos[1], 'confirmations') - assert.property(result.slpUtxos[1], 'satoshis') - assert.property(result.slpUtxos[1], 'isValid') - assert.equal(result.slpUtxos[1].isValid, true) - assert.property(result.slpUtxos[1], 'transactionType') - assert.property(result.slpUtxos[1], 'tokenId') - assert.property(result.slpUtxos[1], 'tokenTicker') - assert.property(result.slpUtxos[1], 'tokenName') - assert.property(result.slpUtxos[1], 'tokenDocumentUrl') - assert.property(result.slpUtxos[1], 'tokenDocumentHash') - assert.property(result.slpUtxos[1], 'decimals') - assert.property(result.slpUtxos[1], 'tokenType') - assert.property(result.slpUtxos[1], 'tokenQty') + assert.property(result.slpUtxos[0].utxos[1], 'txid') + assert.property(result.slpUtxos[0].utxos[1], 'vout') + assert.property(result.slpUtxos[0].utxos[1], 'value') + assert.property(result.slpUtxos[0].utxos[1], 'height') + assert.property(result.slpUtxos[0].utxos[1], 'confirmations') + assert.property(result.slpUtxos[0].utxos[1], 'satoshis') + assert.property(result.slpUtxos[0].utxos[1], 'isValid') + assert.equal(result.slpUtxos[0].utxos[1].isValid, true) + assert.property(result.slpUtxos[0].utxos[1], 'transactionType') + assert.property(result.slpUtxos[0].utxos[1], 'tokenId') + assert.property(result.slpUtxos[0].utxos[1], 'tokenTicker') + assert.property(result.slpUtxos[0].utxos[1], 'tokenName') + assert.property(result.slpUtxos[0].utxos[1], 'tokenDocumentUrl') + assert.property(result.slpUtxos[0].utxos[1], 'tokenDocumentHash') + assert.property(result.slpUtxos[0].utxos[1], 'decimals') + assert.property(result.slpUtxos[0].utxos[1], 'tokenType') + assert.property(result.slpUtxos[0].utxos[1], 'tokenQty') + }) + + it('should process data directly from Electrumx', async () => { + const addrs = [ + 'bitcoincash:qq6mvsm7l92d77zpymmltvaw09p5uzghyuyx7spygg', + 'bitcoincash:qpjdrs8qruzh8xvusdfmutjx62awcepnhyperm3g89', + 'bitcoincash:qzygn28zpgeemnptkn26xzyuzzfu9l8f9vfvq7kptk' + ] + + const utxos = await bchjs.Electrumx.utxo(addrs) + // console.log(`utxos: ${JSON.stringify(utxos, null, 2)}`) + + req.body.utxos = utxos.utxos + const result = await slp.hydrateUtxos(req, res) + // console.log(`result: ${JSON.stringify(result, null, 2)}`) + + // Test the general structure of the output. + assert.isArray(result.slpUtxos) + assert.equal(result.slpUtxos.length, 3) + assert.equal(result.slpUtxos[0].utxos.length, 1) + assert.equal(result.slpUtxos[1].utxos.length, 1) + assert.equal(result.slpUtxos[2].utxos.length, 2) }) }) }) diff --git a/test/v3/slp.js b/test/v3/slp.js index 34a4d36..4c1d517 100644 --- a/test/v3/slp.js +++ b/test/v3/slp.js @@ -938,22 +938,26 @@ describe('#SLP', () => { it('should return utxo details', async () => { const utxos = [ { - txid: - 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', - vout: 3, - value: '6816', - height: 606848, - confirmations: 13, - satoshis: 6816 - }, - { - txid: - 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', - vout: 2, - value: '546', - height: 606848, - confirmations: 13, - satoshis: 546 + utxos: [ + { + txid: + 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', + vout: 3, + value: '6816', + height: 606848, + confirmations: 13, + satoshis: 6816 + }, + { + txid: + 'd56a2b446d8149c39ca7e06163fe8097168c3604915f631bc58777d669135a56', + vout: 2, + value: '546', + height: 606848, + confirmations: 13, + satoshis: 546 + } + ] } ] @@ -998,36 +1002,37 @@ describe('#SLP', () => { // Test the general structure of the output. assert.isArray(result.slpUtxos) - assert.equal(result.slpUtxos.length, 2) + assert.equal(result.slpUtxos.length, 1) + assert.equal(result.slpUtxos[0].utxos.length, 2) // Test the non-slp UTXO. - assert.property(result.slpUtxos[0], 'txid') - assert.property(result.slpUtxos[0], 'vout') - assert.property(result.slpUtxos[0], 'value') - assert.property(result.slpUtxos[0], 'height') - assert.property(result.slpUtxos[0], 'confirmations') - assert.property(result.slpUtxos[0], 'satoshis') - assert.property(result.slpUtxos[0], 'isValid') - assert.equal(result.slpUtxos[0].isValid, false) + assert.property(result.slpUtxos[0].utxos[0], 'txid') + assert.property(result.slpUtxos[0].utxos[0], 'vout') + assert.property(result.slpUtxos[0].utxos[0], 'value') + assert.property(result.slpUtxos[0].utxos[0], 'height') + assert.property(result.slpUtxos[0].utxos[0], 'confirmations') + assert.property(result.slpUtxos[0].utxos[0], 'satoshis') + assert.property(result.slpUtxos[0].utxos[0], 'isValid') + assert.equal(result.slpUtxos[0].utxos[0].isValid, false) // Test the slp UTXO. - assert.property(result.slpUtxos[1], 'txid') - assert.property(result.slpUtxos[1], 'vout') - assert.property(result.slpUtxos[1], 'value') - assert.property(result.slpUtxos[1], 'height') - assert.property(result.slpUtxos[1], 'confirmations') - assert.property(result.slpUtxos[1], 'satoshis') - assert.property(result.slpUtxos[1], 'isValid') - assert.equal(result.slpUtxos[1].isValid, true) - assert.property(result.slpUtxos[1], 'transactionType') - assert.property(result.slpUtxos[1], 'tokenId') - assert.property(result.slpUtxos[1], 'tokenTicker') - assert.property(result.slpUtxos[1], 'tokenName') - assert.property(result.slpUtxos[1], 'tokenDocumentUrl') - assert.property(result.slpUtxos[1], 'tokenDocumentHash') - assert.property(result.slpUtxos[1], 'decimals') - assert.property(result.slpUtxos[1], 'tokenType') - assert.property(result.slpUtxos[1], 'tokenQty') + assert.property(result.slpUtxos[0].utxos[1], 'txid') + assert.property(result.slpUtxos[0].utxos[1], 'vout') + assert.property(result.slpUtxos[0].utxos[1], 'value') + assert.property(result.slpUtxos[0].utxos[1], 'height') + assert.property(result.slpUtxos[0].utxos[1], 'confirmations') + assert.property(result.slpUtxos[0].utxos[1], 'satoshis') + assert.property(result.slpUtxos[0].utxos[1], 'isValid') + assert.equal(result.slpUtxos[0].utxos[1].isValid, true) + assert.property(result.slpUtxos[0].utxos[1], 'transactionType') + assert.property(result.slpUtxos[0].utxos[1], 'tokenId') + assert.property(result.slpUtxos[0].utxos[1], 'tokenTicker') + assert.property(result.slpUtxos[0].utxos[1], 'tokenName') + assert.property(result.slpUtxos[0].utxos[1], 'tokenDocumentUrl') + assert.property(result.slpUtxos[0].utxos[1], 'tokenDocumentHash') + assert.property(result.slpUtxos[0].utxos[1], 'decimals') + assert.property(result.slpUtxos[0].utxos[1], 'tokenType') + assert.property(result.slpUtxos[0].utxos[1], 'tokenQty') }) }) }) From 40f6b17d3b85d575d2d68130244d7bd5de6fae33 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 28 Sep 2020 08:37:34 -0700 Subject: [PATCH 05/22] fix(internal calls): Applying paid rate limits to internal calls --- src/middleware/route-ratelimit.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index 54ae7b6..3c0d891 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -129,6 +129,7 @@ class RateLimits { origin && (origin.toString().indexOf('wallet.fullstack.cash') > -1 || origin.toString().indexOf('sandbox.fullstack.cash') > -1 || + origin.toString().indexOf('172.17.') > -1 || origin === 'slp-api') ) { pointsToConsume = 1 From 61650017904a6a218ceb13d423064286742cbc67 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 28 Sep 2020 18:10:49 -0700 Subject: [PATCH 06/22] fix(hydrateUtxos): Bumping bch-js version to fix hydateUtxos --- package-lock.json | 860 ++++++++++++++++++---------------------------- package.json | 2 +- 2 files changed, 337 insertions(+), 525 deletions(-) diff --git a/package-lock.json b/package-lock.json index 766a8f4..b189cda 100644 --- a/package-lock.json +++ b/package-lock.json @@ -366,9 +366,9 @@ } }, "@psf/bch-js": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-3.5.5.tgz", - "integrity": "sha512-bZs1p6VNDBYJPJuKuYD4h2Wsp+gt38181XlVX+2Yq8JGFOvf5ZRSrkqcd0LYvfiP8f9IA46aaAtTaLQT1KQsJw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-3.6.2.tgz", + "integrity": "sha512-6r8NBB48WgWHAFeuwrlkaE3B87TT3o/8hdjU1DOWXUx7lMFIF1Aq25CffUxF3sHP0pSx3/+4ulQZFROHGTwnDQ==", "requires": { "@uppy/core": "^1.10.4", "@uppy/tus": "^1.5.12", @@ -437,27 +437,6 @@ } } }, - "bitcoincashjs-lib": { - "version": "github:christroutner/bitcoincashjs-lib#715823b5d6eb3e2961e715605ad1464f3223da0b", - "from": "github:christroutner/bitcoincashjs-lib#master", - "requires": { - "bech32": "^1.1.2", - "bigi": "^1.4.0", - "bip66": "^1.1.0", - "bitcoincash-ops": "github:christroutner/bitcoincash-ops", - "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.18.0", - "varuint-bitcoin": "^1.0.4", - "wif": "^2.0.1" - } - }, "fs-extra": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", @@ -888,11 +867,6 @@ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "dev": true }, - "@types/tus-js-client": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@types/tus-js-client/-/tus-js-client-1.8.0.tgz", - "integrity": "sha512-lWxu5+6qfyfwsW99GzUeJ9y9JeOSSLduKxgYMvaYM7sGTDKZsrIIHTUbHI2P016xhXtu9NxmUM3GrB4i14ie4A==" - }, "@types/ws": { "version": "7.2.6", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-7.2.6.tgz", @@ -902,22 +876,22 @@ } }, "@uppy/companion-client": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@uppy/companion-client/-/companion-client-1.4.5.tgz", - "integrity": "sha512-lHmw48rWpipLKBDoDVuMPkSWZPIPoQr9aaV3VSAsVIUg65IhYyJ0FFBEXCh8jtNZsQbkHuXE+xM0niD0kDH/5w==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@uppy/companion-client/-/companion-client-1.5.4.tgz", + "integrity": "sha512-AmrKamjHraqm/eV00ps8brw5ajI9NqjzBotTtPH9QaC+D3wIQ/EoWZNTGx4OlGYRahEoVr1K6hhlyUoHmEQc4w==", "requires": { - "@uppy/utils": "^3.0.0", + "@uppy/utils": "^3.2.3", "namespace-emitter": "^2.0.1" } }, "@uppy/core": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/@uppy/core/-/core-1.10.5.tgz", - "integrity": "sha512-RweK/RSJAf5iEjBPt+Vxspnnavkvq2yI2OuAm9Sfz+035k+IPi8G59z0RcBVpWxYZrPkd1lxbd+9Bwql4P8JLw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@uppy/core/-/core-1.13.2.tgz", + "integrity": "sha512-h0fbwlntm51lJ5i9k16csAsGNuWSzAZe69gLX/5DOISWqziRdF3g2EtSG17p/p/tK/MwX1dzyNU8JI6krDYIwA==", "requires": { "@transloadit/prettier-bytes": "0.0.7", - "@uppy/store-default": "^1.2.1", - "@uppy/utils": "^3.0.0", + "@uppy/store-default": "^1.2.4", + "@uppy/utils": "^3.2.3", "cuid": "^2.1.1", "lodash.throttle": "^4.1.1", "mime-match": "^1.0.2", @@ -926,26 +900,26 @@ } }, "@uppy/store-default": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@uppy/store-default/-/store-default-1.2.1.tgz", - "integrity": "sha512-B7iKa9CMLTPgSu1lHQX+Z82NgxoBrzWfZg9WbZnvoJRHnoiwu8A3EFXPLq0OEslvJndBD4xPgUms0BPt4WbnWw==" + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@uppy/store-default/-/store-default-1.2.4.tgz", + "integrity": "sha512-d4YTq6SeH792+vG9OXGCfmCIoo4RteLJKU2mZCzMYNjCZ1yNfU/FVw1r7heTc7f2wNls56ABor2xSybk/X+wFA==" }, "@uppy/tus": { - "version": "1.5.13", - "resolved": "https://registry.npmjs.org/@uppy/tus/-/tus-1.5.13.tgz", - "integrity": "sha512-sjwHTtPsG0MHieTMfNS8qi/SHY2lkVUAIHXzaLwRwUW2PXatGbHXeAfTs5Wb7b5HZivMKmlqnWHWvD4Em4vY2g==", + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@uppy/tus/-/tus-1.7.6.tgz", + "integrity": "sha512-Aetmw/EjwXuUl6XoosiFbqSk59iRuTfsJmKoWL+TI4PiE+NPVYreRao7vS1Dy072cQo7nD9MlK/tAFOZ/Lf7DQ==", "requires": { - "@types/tus-js-client": "^1.8.0", - "@uppy/companion-client": "^1.4.5", - "@uppy/utils": "^3.0.0", - "tus-js-client": "^1.8.0" + "@uppy/companion-client": "^1.5.4", + "@uppy/utils": "^3.2.3", + "tus-js-client": "^2.1.1" } }, "@uppy/utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@uppy/utils/-/utils-3.0.0.tgz", - "integrity": "sha512-QOXy2TrObmFBVOBFekQ3zgr7cKp6v5jUUSPBxN5GzxE9I8FDH6tOtDU4FwzatsC5g430e68DHEyiM1IgA1tTvA==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@uppy/utils/-/utils-3.2.3.tgz", + "integrity": "sha512-ue14v4yKK6bYBPGjt31wvLnYMThKCVOsy3R7y++eFuPsxfidIGmOGiv+Qmv0895kMJtt7gk0MddXjbCdBX4Ksg==", "requires": { + "abortcontroller-polyfill": "^1.4.0", "lodash.throttle": "^4.1.1" } }, @@ -964,6 +938,11 @@ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, + "abortcontroller-polyfill": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.5.0.tgz", + "integrity": "sha512-O6Xk757Jb4o0LMzMOMdWvxpHWrQzruYBaUruFaIOfAQRnWFxfdXYobw12jrVHGtoXk6WiiyYzc0QWN9aL62HQA==" + }, "abstract-leveldown": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-6.2.2.tgz", @@ -1693,9 +1672,9 @@ } }, "bchaddrjs-slp": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/bchaddrjs-slp/-/bchaddrjs-slp-0.2.8.tgz", - "integrity": "sha512-Is4NQG0xhrmW8ml40n8IjQzYlg1gMqst2RkF20SWjuhiSA8VDx1F53krthFqZFp5hpB+Ig/NPx6NX4VEH9W32Q==", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/bchaddrjs-slp/-/bchaddrjs-slp-0.2.9.tgz", + "integrity": "sha512-+jWviCay+q8Kg73EpsKwHGipX3VBpyil4uGdLKEgj88sNqAM1fL7WZO0g+FbASF/n+TCYCPkBbM5TznRD1yHuA==", "requires": { "bs58check": "^2.1.2", "cashaddrjs-slp": "^0.2.12" @@ -2096,17 +2075,6 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, "locate-path": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", @@ -2145,37 +2113,6 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=" }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "requires": { - "error-ex": "^1.2.0" - } - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "requires": { - "pify": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, "read-pkg-up": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", @@ -2310,10 +2247,31 @@ "resolved": "https://registry.npmjs.org/bitcoincashjs/-/bitcoincashjs-0.1.14.tgz", "integrity": "sha512-x0wMVSCZ56ZQnSQ+Xim7XfS0IYiOyOwo0pdMCmR+cDNB7zRhICFuH5i3lNdLSgUw1e7Pc3FmUlC9wruQNlYMnA==" }, + "bitcoincashjs-lib": { + "version": "github:christroutner/bitcoincashjs-lib#715823b5d6eb3e2961e715605ad1464f3223da0b", + "from": "github:christroutner/bitcoincashjs-lib#master", + "requires": { + "bech32": "^1.1.2", + "bigi": "^1.4.0", + "bip66": "^1.1.0", + "bitcoincash-ops": "github:christroutner/bitcoincash-ops", + "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.18.0", + "varuint-bitcoin": "^1.0.4", + "wif": "^2.0.1" + } + }, "bitcoinjs-message": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/bitcoinjs-message/-/bitcoinjs-message-2.1.0.tgz", - "integrity": "sha512-xVL2YvyAJUI8ZwfNpi6Ju5zda3+QYGHTLUvISDb0VHWbsWn9Zyvd1o8XHRC/0r+DNwDIwenpXDSPl1XLCMGnMA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/bitcoinjs-message/-/bitcoinjs-message-2.1.3.tgz", + "integrity": "sha512-WKxkDJF7skE4yFWIqdAC2hESlyaf5TQakPGvNPp91borRETRAyUes1+p0qS264+gMt6hFsx5WX7ifFKdKdlzGg==", "requires": { "bech32": "^1.1.3", "bs58check": "^2.1.2", @@ -2916,9 +2874,9 @@ "dev": true }, "cashaddrjs": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/cashaddrjs/-/cashaddrjs-0.3.11.tgz", - "integrity": "sha512-TXbSJVCPa5ER9jRId0S1ZgxvAqLYowciCpgzDC66hYarQKeDRZ5wvjAa8xKPZwLEDI8t5fBL3f8PyPzz7PoMnA==", + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/cashaddrjs/-/cashaddrjs-0.3.12.tgz", + "integrity": "sha512-GdjCYMVwd86HXcFcxyEZQLPLFv8a/u0ccYPsO0PpnUW26LhZzHX9l9QA+DjaeUah7tnebwPs33NWDbbUy8iVYQ==", "requires": { "big-integer": "1.6.36" }, @@ -3996,21 +3954,21 @@ }, "dependencies": { "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", "object-inspect": "^1.7.0", "object-keys": "^1.1.1", "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" } }, "es-to-primitive": { @@ -4029,9 +3987,9 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" }, "is-date-object": { "version": "1.0.2", @@ -4039,11 +3997,11 @@ "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" } }, "isarray": { @@ -4052,29 +4010,9 @@ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" } } }, @@ -4503,9 +4441,9 @@ } }, "engine.io": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.1.tgz", - "integrity": "sha512-8MfIfF1/IIfxuc2gv5K+XlFZczw/BpTvqBdl0E2fBLkYQp4miv4LuDTVtYt4yMyaIFLEr4vtaSgV4mjvll8Crw==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.4.2.tgz", + "integrity": "sha512-b4Q85dFkGw+TqgytGPrGgACRUhsdKc9S9ErRAXpPGy/CXKs4tYoHDkvIRdsseAF7NjfVwjRFIn6KTnbw7LwJZg==", "requires": { "accepts": "~1.3.4", "base64id": "2.0.0", @@ -4516,9 +4454,9 @@ } }, "engine.io-client": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.2.tgz", - "integrity": "sha512-AWjc1Xg06a6UPFOBAzJf48W1UR/qKYmv/ubgSCumo9GXgvL/xGIvo05dXoBL+2NTLMipDI7in8xK61C17L25xg==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.3.tgz", + "integrity": "sha512-0NGY+9hioejTEJCaSJZfWZLk4FPI9dN+1H1C4+wj2iuFba47UgZbJzfWs4aNFajnX/qAaYKbe2lLTfEEWzCmcw==", "requires": { "component-emitter": "~1.3.0", "component-inherit": "0.0.3", @@ -4700,21 +4638,21 @@ }, "dependencies": { "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", "object-inspect": "^1.7.0", "object-keys": "^1.1.1", "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" } }, "es-to-primitive": { @@ -4733,16 +4671,16 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" } }, "isarray": { @@ -4751,29 +4689,9 @@ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" } } }, @@ -5996,9 +5914,9 @@ "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==" }, "figlet": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.4.0.tgz", - "integrity": "sha512-CxxIjEKHlqGosgXaIA+sikGDdV6KZOOlzPJnYuPgQlOSHZP5h9WIghYI30fyXnwEVeSH7Hedy72gC6zJrFC+SQ==" + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/figlet/-/figlet-1.5.0.tgz", + "integrity": "sha512-ZQJM4aifMpz6H19AW1VqvZ7l4pOE9p7i/3LyxgO2kp+PO/VcDYNqIHEMtkccqIhTXMKci4kjueJr/iCQEaT/Ww==" }, "figures": { "version": "2.0.0", @@ -7317,6 +7235,11 @@ "resolved": "https://registry.npmjs.org/is-negated-glob/-/is-negated-glob-1.0.0.tgz", "integrity": "sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI=" }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=" + }, "is-npm": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", @@ -7450,21 +7373,21 @@ }, "dependencies": { "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", "object-inspect": "^1.7.0", "object-keys": "^1.1.1", "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" } }, "es-to-primitive": { @@ -7483,42 +7406,22 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" } }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" } } }, @@ -7711,9 +7614,9 @@ "dev": true }, "js-base64": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz", - "integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==" + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz", + "integrity": "sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==" }, "js-sha256": { "version": "0.9.0", @@ -12960,21 +12863,21 @@ }, "dependencies": { "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", "object-inspect": "^1.7.0", "object-keys": "^1.1.1", "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" } }, "es-to-primitive": { @@ -12993,42 +12896,22 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" } }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" } } }, @@ -13469,11 +13352,11 @@ "dev": true }, "p-event": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.1.0.tgz", - "integrity": "sha512-4vAd06GCsgflX4wHN1JqrMzBh/8QZ4j+rzp0cd2scXRwuBEv+QR3wrVA5aLhWDLw4y2WgDKvzWF3CCLmVM1UgA==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", "requires": { - "p-timeout": "^2.0.1" + "p-timeout": "^3.1.0" } }, "p-filter": { @@ -13535,9 +13418,9 @@ } }, "p-timeout": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", - "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", "requires": { "p-finally": "^1.0.0" } @@ -13700,6 +13583,14 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "requires": { + "pify": "^2.0.0" + } + }, "pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", @@ -13712,9 +13603,9 @@ "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" }, "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", "requires": { "create-hash": "^1.1.2", "create-hmac": "^1.1.4", @@ -13804,6 +13695,11 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==" }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, "pkg-conf": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-2.1.0.tgz", @@ -14153,9 +14049,9 @@ "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" }, "querystringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", - "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" }, "quick-lru": { "version": "4.0.1", @@ -14220,6 +14116,37 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz", "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==" }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + }, + "dependencies": { + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "requires": { + "error-ex": "^1.2.0" + } + } + } + }, "read-pkg-up": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", @@ -14391,21 +14318,21 @@ }, "dependencies": { "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", "object-inspect": "^1.7.0", "object-keys": "^1.1.1", "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" } }, "es-to-primitive": { @@ -14424,42 +14351,22 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" } }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" } } }, @@ -15215,30 +15122,31 @@ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" }, "side-channel": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.2.tgz", - "integrity": "sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.3.tgz", + "integrity": "sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==", "requires": { - "es-abstract": "^1.17.0-next.1", - "object-inspect": "^1.7.0" + "es-abstract": "^1.18.0-next.0", + "object-inspect": "^1.8.0" }, "dependencies": { "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "version": "1.18.0-next.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz", + "integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==", "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.0", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" } }, "es-to-primitive": { @@ -15257,42 +15165,22 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" } }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" } } }, @@ -16032,31 +15920,31 @@ } }, "string.prototype.trim": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz", - "integrity": "sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.2.tgz", + "integrity": "sha512-b5yrbl3BXIjHau9Prk7U0RRYcUYdN4wGSVaqoBQS50CCE3KBuYU0TYRNPFCP7aVoNMX87HKThdMRVIP3giclKg==", "requires": { "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1" + "es-abstract": "^1.18.0-next.0" }, "dependencies": { "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "version": "1.18.0-next.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz", + "integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==", "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.0", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" } }, "es-to-primitive": { @@ -16075,42 +15963,22 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" } }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" } } }, @@ -16124,21 +15992,21 @@ }, "dependencies": { "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", "object-inspect": "^1.7.0", "object-keys": "^1.1.1", "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" } }, "es-to-primitive": { @@ -16157,42 +16025,22 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" } }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" } } }, @@ -16226,21 +16074,21 @@ }, "dependencies": { "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", "object-inspect": "^1.7.0", "object-keys": "^1.1.1", "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" } }, "es-to-primitive": { @@ -16259,42 +16107,22 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" } }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" } } }, @@ -16420,18 +16248,23 @@ "path-is-absolute": "^1.0.0" } }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" } }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" }, "resolve": { "version": "1.17.0", @@ -16701,14 +16534,13 @@ } }, "tus-js-client": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/tus-js-client/-/tus-js-client-1.8.0.tgz", - "integrity": "sha512-qPX3TywqzxocTxUZtcS8X7Aik72SVMa0jKi4hWyfvRV+s9raVzzYGaP4MoJGaF0yOgm2+b6jXaVEHogxcJ8LGw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tus-js-client/-/tus-js-client-2.2.0.tgz", + "integrity": "sha512-6RM7SHJD1j3X4o+f8dX1tcPOETsSitbF+ee3Ecz4Lu5+muYJnyYMRUXbz12N7dDfoCQ14Y5EmksbDP4BGzmC8w==", "requires": { "buffer-from": "^0.1.1", "combine-errors": "^3.0.3", - "extend": "^3.0.2", - "js-base64": "^2.4.9", + "js-base64": "^2.6.1", "lodash.throttle": "^4.1.1", "proper-lockfile": "^2.0.1", "url-parse": "^1.4.3" @@ -17241,21 +17073,21 @@ }, "dependencies": { "es-abstract": { - "version": "1.17.5", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz", - "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", "object-inspect": "^1.7.0", "object-keys": "^1.1.1", "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" } }, "es-to-primitive": { @@ -17274,42 +17106,22 @@ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" } }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" - }, - "string.prototype.trimleft": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz", - "integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimstart": "^1.0.0" - } - }, - "string.prototype.trimright": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz", - "integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5", - "string.prototype.trimend": "^1.0.0" - } + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" } } }, @@ -17578,9 +17390,9 @@ } }, "ws": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz", - "integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w==" + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" }, "x-xss-protection": { "version": "1.3.0", diff --git a/package.json b/package.json index f63a082..6eaab53 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "node": ">=10.15.1" }, "dependencies": { - "@psf/bch-js": "^3.5.5", + "@psf/bch-js": "^3.6.2", "apidoc": "^0.23.0", "axios": "^0.19.0", "bitcore-lib-cash": "^8.20.3", From d67b3aa052cd8d66392443f3caadaf73934174bd Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 28 Sep 2020 18:32:43 -0700 Subject: [PATCH 07/22] fix(rate limits): Increasing internal 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 3c0d891..a53b52d 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -129,13 +129,18 @@ class RateLimits { origin && (origin.toString().indexOf('wallet.fullstack.cash') > -1 || origin.toString().indexOf('sandbox.fullstack.cash') > -1 || - origin.toString().indexOf('172.17.') > -1 || origin === 'slp-api') ) { pointsToConsume = 1 res.locals.pointsToConsume = pointsToConsume // Feedback for tests. } + // Apply paid-access rate limits based on key/IP + if (key.toString().indexOf('172.17.') > -1) { + pointsToConsume = 1 + res.locals.pointsToConsume = pointsToConsume // Feedback for tests. + } + wlogger.info( `User ${key} consuming ${pointsToConsume} point for resource ${resource}.` ) From f4f5f02a2ffd5816df71b3079caa8d406e11fef9 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 28 Sep 2020 19:45:47 -0700 Subject: [PATCH 08/22] fix(bch-js): bumping to version 3.6.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6eaab53..38b7c23 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "node": ">=10.15.1" }, "dependencies": { - "@psf/bch-js": "^3.6.2", + "@psf/bch-js": "^3.6.3", "apidoc": "^0.23.0", "axios": "^0.19.0", "bitcore-lib-cash": "^8.20.3", From 10eb4da1128e4656b1a273dbc51a66d2738473b3 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 28 Sep 2020 19:49:49 -0700 Subject: [PATCH 09/22] fix(rate limits): Increasing rate limits for internal calls --- src/middleware/route-ratelimit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index a53b52d..9fc7fe9 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -137,7 +137,7 @@ class RateLimits { // Apply paid-access rate limits based on key/IP if (key.toString().indexOf('172.17.') > -1) { - pointsToConsume = 1 + pointsToConsume = 0.1 res.locals.pointsToConsume = pointsToConsume // Feedback for tests. } From 9e3dfd98487766bb6888240af3f3052982cd7723 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 28 Sep 2020 19:55:28 -0700 Subject: [PATCH 10/22] Updating package lock --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index b189cda..710a89e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -366,9 +366,9 @@ } }, "@psf/bch-js": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-3.6.2.tgz", - "integrity": "sha512-6r8NBB48WgWHAFeuwrlkaE3B87TT3o/8hdjU1DOWXUx7lMFIF1Aq25CffUxF3sHP0pSx3/+4ulQZFROHGTwnDQ==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-3.6.3.tgz", + "integrity": "sha512-0nqmLprZU+1lh1WTnAqVHQD+bIPJIcEQ36rBNoyh4ejzWZoQ7pTMj2r2I7JGhWmxWdSQ+g4stT1OrC4qmH6jdw==", "requires": { "@uppy/core": "^1.10.4", "@uppy/tus": "^1.5.12", From 698ac3825106b8fd66ef1bc445da832dc8d7f473 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Mon, 28 Sep 2020 20:17:07 -0700 Subject: [PATCH 11/22] fix(rate limits): Fixing hydrateUtxos rate limits --- src/middleware/route-ratelimit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index 9fc7fe9..a53b52d 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -137,7 +137,7 @@ class RateLimits { // Apply paid-access rate limits based on key/IP if (key.toString().indexOf('172.17.') > -1) { - pointsToConsume = 0.1 + pointsToConsume = 1 res.locals.pointsToConsume = pointsToConsume // Feedback for tests. } From 22d7a2b7a2a4c2ba4901de207ca57c62a362c252 Mon Sep 17 00:00:00 2001 From: Rosco Kalis Date: Wed, 30 Sep 2020 20:52:13 -0400 Subject: [PATCH 12/22] Add POST /tx/broadcast endpoint + tests --- src/routes/v3/electrumx.js | 87 ++++++++++++++++++++++++++++++++++++++ test/v3/a01-electrumx.js | 86 +++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+) diff --git a/src/routes/v3/electrumx.js b/src/routes/v3/electrumx.js index 8e30e7b..591d285 100644 --- a/src/routes/v3/electrumx.js +++ b/src/routes/v3/electrumx.js @@ -52,6 +52,7 @@ class Electrum { _this.router.post('/utxos', _this.utxosBulk) _this.router.get('/tx/data/:txid', _this.getTransactionDetails) _this.router.post('/tx/data', _this.transactionDetailsBulk) + _this.router.post('/tx/broadcast', _this.broadcastTransaction) _this.router.get('/block/headers/:height', _this.getBlockHeaders) _this.router.post('/block/headers', _this.blockHeadersBulk) _this.router.get('/balance/:address', _this.getBalance) @@ -491,6 +492,92 @@ class Electrum { } } + // Returns a promise that resolves to transaction ID of the broadcasted transaction. + // Expects input to be a txHex string, and input validation to have already + // been done by parent, calling function. + async _broadcastTransactionWithElectrum (txHex) { + try { + if (!_this.isReady) { + throw new Error( + 'ElectrumX server connection is not ready. Call await connectToServer() first.' + ) + } + + // Broadcast the transaction hex to the ElectrumX server. + const electrumResponse = await _this.electrumx.request( + 'blockchain.transaction.broadcast', + txHex + ) + // console.log( + // `electrumResponse: ${JSON.stringify(electrumResponse, null, 2)}` + // ) + + return electrumResponse + } catch (err) { + // console.log('err: ', err) + + // Write out error to error log. + wlogger.error( + 'Error in elecrumx.js/_transactionDetailsFromElectrum(): ', + err + ) + throw err + } + } + + /** + * @api {post} /electrumx/tx/broadcast Broadcast a raw transaction + * @apiName Broadcast a raw transaction + * @apiGroup ElectrumX / Fulcrum + * @apiDescription Broadcast a raw transaction and return the transaction on success or error on failure. + * + * @apiExample Example usage: + * curl -X POST "https://api.fullstack.cash/v3/electrumx/tx/broadcast" -H "accept: application/json" -H "Content-Type: application/json" -d '020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000' + * + */ + // POST handler for broadcasting a single transaction + async broadcastTransaction (req, res, next) { + try { + const txHex = req.body + + if (typeof txHex !== 'string') { + res.status(400) + return res.json({ + success: false, + error: 'request body must be a string.' + }) + } + + wlogger.debug( + 'Executing electrumx/broadcastTransaction with this tx hex: ', + txHex + ) + + // Get data from ElectrumX server. + const electrumResponse = await _this._broadcastTransactionWithElectrum(txHex) + // console.log(`_utxosFromElectrumx(): ${JSON.stringify(electrumResponse, null, 2)}`) + + // Pass the error message if ElectrumX reports an error. + if (electrumResponse instanceof Error) { + res.status(400) + return res.json({ + success: false, + error: electrumResponse.message + }) + } + + res.status(200) + return res.json({ + success: true, + txid: electrumResponse + }) + } catch (err) { + wlogger.error('Error in electrumx.js/broadcastTransaction().', err) + + return _this.errorHandler(err, res) + } + } + // Returns a promise that resolves to block header data for a block height. // Expects input to be a height number, and input validation to have already // been done by parent, calling function. diff --git a/test/v3/a01-electrumx.js b/test/v3/a01-electrumx.js index 7492568..7c777fe 100644 --- a/test/v3/a01-electrumx.js +++ b/test/v3/a01-electrumx.js @@ -906,6 +906,92 @@ describe('#ElectrumX Router', () => { }) }) + describe.only('#_broadcastTransactionWithElectrum', () => { + it('should return error object for invalid formatted transaction', async () => { + const invalidHex = mockData.txDetails.hex.substring(10) + + stubMethodForUnitTests( + electrumxRoute.electrumx, + 'request', + new Error('Error: the transaction was rejected by network rules.\n\nTX decode failed\n') + ) + + const result = await electrumxRoute._broadcastTransactionWithElectrum(invalidHex) + + assert.instanceOf(result, Error) + assert.include(result.message, 'TX decode failed') + }) + + it('should return txid for valid transaction', async function () { + // We cannot send an actual broadcast transaction to mainnet + if (process.env.TEST !== 'unit') return this.skip() + + const validHex = mockData.txDetails.hex + + stubMethodForUnitTests( + electrumxRoute.electrumx, + 'request', + mockData.txDetails.hash + ) + + const result = await electrumxRoute._broadcastTransactionWithElectrum(validHex) + + assert.typeOf(result, 'string') + assert.equal(result, mockData.txDetails.hash) + }) + }) + + describe.only('#broadcastTransaction', () => { + it('should throw an error for a non-string', async () => { + req.body = 456 + + stubMethodForUnitTests( + electrumxRoute, + '_broadcastTransactionWithElectrum', + new Error('request body must be a string.') + ) + + const result = await electrumxRoute.broadcastTransaction(req, res) + + expectRouteError(res, result, 'request body must be a string.') + }) + + it('should throw an error object for invalid formatted transaction', async () => { + req.body = mockData.txDetails.hex.substring(10) + + stubMethodForUnitTests( + electrumxRoute, + '_broadcastTransactionWithElectrum', + new Error('Error: the transaction was rejected by network rules.\n\nTX decode failed\n') + ) + + const result = await electrumxRoute.broadcastTransaction(req, res) + + expectRouteError(res, result, 'TX decode failed') + }) + + it('should return txid for valid transaction', async function () { + // We cannot send an actual broadcast transaction to mainnet + if (process.env.TEST !== 'unit') return this.skip() + + req.body = mockData.txDetails.hex + + stubMethodForUnitTests( + electrumxRoute, + '_broadcastTransactionWithElectrum', + mockData.txDetails.hash + ) + + const result = await electrumxRoute.broadcastTransaction(req, res) + + assert.property(result, 'success') + assert.equal(result.success, true) + + assert.property(result, 'txid') + assert.equal(result.txid, mockData.txDetails.hash) + }) + }) + describe('#_balanceFromElectrumx', () => { it('should throw error for invalid address', async () => { try { From 085618470fc754476e4cb11550b278cc7595a0e5 Mon Sep 17 00:00:00 2001 From: Rosco Kalis Date: Wed, 30 Sep 2020 20:55:43 -0400 Subject: [PATCH 13/22] Update comments --- src/routes/v3/electrumx.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/routes/v3/electrumx.js b/src/routes/v3/electrumx.js index 591d285..0c6ca7b 100644 --- a/src/routes/v3/electrumx.js +++ b/src/routes/v3/electrumx.js @@ -492,7 +492,7 @@ class Electrum { } } - // Returns a promise that resolves to transaction ID of the broadcasted transaction. + // Returns a promise that resolves to transaction ID of the broadcasted transaction or an error. // Expects input to be a txHex string, and input validation to have already // been done by parent, calling function. async _broadcastTransactionWithElectrum (txHex) { @@ -529,10 +529,10 @@ class Electrum { * @api {post} /electrumx/tx/broadcast Broadcast a raw transaction * @apiName Broadcast a raw transaction * @apiGroup ElectrumX / Fulcrum - * @apiDescription Broadcast a raw transaction and return the transaction on success or error on failure. + * @apiDescription Broadcast a raw transaction and return the transaction ID on success or error on failure. * * @apiExample Example usage: - * curl -X POST "https://api.fullstack.cash/v3/electrumx/tx/broadcast" -H "accept: application/json" -H "Content-Type: application/json" -d '020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000' + * curl -X POST "https://api.fullstack.cash/v3/electrumx/tx/broadcast" -H "accept: application/json" -H "Content-Type: text/plain" -d '020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000' * */ // POST handler for broadcasting a single transaction From e97a0be456336e50cf7a3d0030734e728dbbfec1 Mon Sep 17 00:00:00 2001 From: Rosco Kalis Date: Thu, 1 Oct 2020 06:53:46 -0400 Subject: [PATCH 14/22] Remove .only --- test/v3/a01-electrumx.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/v3/a01-electrumx.js b/test/v3/a01-electrumx.js index 7c777fe..db5936a 100644 --- a/test/v3/a01-electrumx.js +++ b/test/v3/a01-electrumx.js @@ -906,7 +906,7 @@ describe('#ElectrumX Router', () => { }) }) - describe.only('#_broadcastTransactionWithElectrum', () => { + describe('#_broadcastTransactionWithElectrum', () => { it('should return error object for invalid formatted transaction', async () => { const invalidHex = mockData.txDetails.hex.substring(10) @@ -941,7 +941,7 @@ describe('#ElectrumX Router', () => { }) }) - describe.only('#broadcastTransaction', () => { + describe('#broadcastTransaction', () => { it('should throw an error for a non-string', async () => { req.body = 456 From 7339b8554f4ea972e464c3519ca917c49b4c6809 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Thu, 1 Oct 2020 17:25:52 -0700 Subject: [PATCH 15/22] fix(basic auth): Disabling Basic Authentication --- src/middleware/auth.js | 92 +++++++++--------- src/middleware/route-ratelimit.js | 22 ++--- test/v3/integration/rate-limits.js | 151 +++++++++++++++-------------- 3 files changed, 133 insertions(+), 132 deletions(-) diff --git a/src/middleware/auth.js b/src/middleware/auth.js index 128e9ba..32fae28 100644 --- a/src/middleware/auth.js +++ b/src/middleware/auth.js @@ -22,7 +22,7 @@ 'use strict' const passport = require('passport') -const BasicStrategy = require('passport-http').BasicStrategy +// const BasicStrategy = require('passport-http').BasicStrategy const AnonymousStrategy = require('passport-anonymous') const wlogger = require('../util/winston-logging') @@ -48,55 +48,55 @@ class AuthMW { passport.use(new AnonymousStrategy()) // Initialize passport for 'basic' authentication. - passport.use( - new BasicStrategy({ passReqToCallback: true }, function ( - req, - username, - password, - done - ) { - // console.log(`req: ${util.inspect(req)}`) - // console.log(`username: ${username}`) - // console.log(`password: ${password}`) - - // Create the req.locals property if it does not yet exist. - 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 - - // Evaluate the username and password and set the rate limit accordingly. - // if (username === "BITBOX" && password === PRO_PASS) { - if (username === 'BITBOX') { - for (let i = 0; i < PRO_PASS.length; i++) { - const thisPass = PRO_PASS[i] - - if (password === thisPass) { - wlogger.verbose(`${req.url} called by ${password.slice(0, 6)}`) - - // Success - req.locals.proLimit = true - break - } - } - } - - // console.log(`req.locals: ${util.inspect(req.locals)}`) - - return done(null, true) - }) - ) + // passport.use( + // new BasicStrategy({ passReqToCallback: true }, function ( + // req, + // username, + // password, + // done + // ) { + // // console.log(`req: ${util.inspect(req)}`) + // // console.log(`username: ${username}`) + // // console.log(`password: ${password}`) + // + // // Create the req.locals property if it does not yet exist. + // 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 + // + // // Evaluate the username and password and set the rate limit accordingly. + // // if (username === "BITBOX" && password === PRO_PASS) { + // if (username === 'BITBOX') { + // for (let i = 0; i < PRO_PASS.length; i++) { + // const thisPass = PRO_PASS[i] + // + // if (password === thisPass) { + // wlogger.verbose(`${req.url} called by ${password.slice(0, 6)}`) + // + // // Success + // req.locals.proLimit = true + // break + // } + // } + // } + // + // // console.log(`req.locals: ${util.inspect(req.locals)}`) + // + // return done(null, true) + // }) + // ) } // Middleware called by the route. mw () { - return passport.authenticate(['basic', 'anonymous'], { + return passport.authenticate(['anonymous'], { session: false }) } diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index a53b52d..2576500 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -20,7 +20,7 @@ const redisClient = new Redis(redisOptions) const { RateLimiterRedis } = require('rate-limiter-flexible') const rateLimitOptions = { storeClient: redisClient, - points: 100, // Number of points + points: 1000, // Number of points duration: 60 // Per minute (per 60 seconds) } @@ -98,8 +98,8 @@ class RateLimits { wlogger.debug('No JWT token found!') } - // Used for displaying error message. Default value is 3. - let rateLimit = 3 + // Used for displaying error message. Default value is 30. + let rateLimit = 30 // Code here for the rate limiter is adapted from this example: // https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#authorized-and-not-authorized-users @@ -131,7 +131,7 @@ class RateLimits { origin.toString().indexOf('sandbox.fullstack.cash') > -1 || origin === 'slp-api') ) { - pointsToConsume = 1 + pointsToConsume = 10 res.locals.pointsToConsume = pointsToConsume // Feedback for tests. } @@ -145,7 +145,7 @@ class RateLimits { `User ${key} consuming ${pointsToConsume} point for resource ${resource}.` ) - rateLimit = Math.floor(100 / pointsToConsume) + rateLimit = Math.floor(1000 / pointsToConsume) // Update the key so that rate limits track both the user and the resource. key = `${key}-${resource}` @@ -175,7 +175,7 @@ class RateLimits { // Calculates the points consumed, based on the jwt information and the route // requested. calcPoints (jwtInfo) { - let retVal = 30 // By default, use anonymous tier. + let retVal = 300 // By default, use anonymous tier. try { // console.log(`jwtInfo: ${JSON.stringify(jwtInfo, null, 2)}`) @@ -194,20 +194,20 @@ class RateLimits { if (level40Routes.includes(resource)) { if (apiLevel >= 40) retVal = 1 // else if (apiLevel >= 10) retVal = 10 - else retVal = 10 + else retVal = 100 // Normal indexer routes } else if (level30Routes.includes(resource)) { if (apiLevel >= 30) retVal = 1 - else retVal = 10 + else retVal = 100 // Full node tier } else if (apiLevel >= 20) { - retVal = 1 + retVal = 10 // Free tier, full node only. } else { - retVal = 10 + retVal = 100 } } @@ -215,7 +215,7 @@ class RateLimits { } catch (err) { wlogger.error('Error in route-ratelimit.js/calcPoints()') // throw err - retVal = 30 + retVal = 300 } return retVal diff --git a/test/v3/integration/rate-limits.js b/test/v3/integration/rate-limits.js index c26790b..cb4c65f 100644 --- a/test/v3/integration/rate-limits.js +++ b/test/v3/integration/rate-limits.js @@ -16,9 +16,10 @@ util.inspect.defaultOptions = { depth: 1 } // const SERVER = `http://192.168.0.36:12400/v3/` const SERVER = 'http://localhost:3000/v3/' +// const SERVER = 'https://api.fullstack.cash/v3/' const TEST_JWT = - 'eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlM2EwNDE1ZWIyOWE5NjJkYTI3MDhiNCIsImFwaUxldmVsIjowLCJyYXRlTGltaXQiOjEwLCJpYXQiOjE1ODA4NjA0NjcsImV4cCI6MTU4MzQ1MjQ2N30.fuY5S-YrF0J11h5uyMjPe7wiVkYRnIyXi4dL9-V-C6pLJm33p0dSq_pSheVVWw78n5kAvL_9kFHngbnmQiOJYQ' + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2JmMDIyMWMxMDAxMmFkOTNmZiIsImVtYWlsIjoiY2hyaXMudHJvdXRuZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjQwLCJyYXRlTGltaXQiOjMsImlhdCI6MTYwMDYyODk1MSwiZXhwIjoxNjAzMjIwOTUxfQ.JPXDJQsxJFtCGZjHOd-hRfJuY41Ef_FQ4ET06CtYdNk' describe('#rate limits', () => { it('should get control/getNetworkInfo() with no auth', async () => { @@ -35,7 +36,7 @@ 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 5 request per minute', async () => { try { // Actual rate limit is 60 per minute X 4 nodes = 240 rpm. const options = { @@ -44,7 +45,7 @@ describe('#rate limits', () => { } const promises = [] - for (let i = 0; i < 30; i++) { + for (let i = 0; i < 5; i++) { const promise = axios(options) promises.push(promise) } @@ -60,79 +61,79 @@ describe('#rate limits', () => { } }) - it('should not trigger rate-limit handler if correct pro-tier password is used', async () => { - try { - const username = 'BITBOX' + // 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?' + // ) + // } + // }) - // 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 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 { From d56e6d6b3a8c1a78e5952f3fa1400a2bcc5e5cf3 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Thu, 1 Oct 2020 17:26:31 -0700 Subject: [PATCH 16/22] linting --- src/middleware/auth.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/middleware/auth.js b/src/middleware/auth.js index 32fae28..34b6a4a 100644 --- a/src/middleware/auth.js +++ b/src/middleware/auth.js @@ -24,7 +24,7 @@ const passport = require('passport') // const BasicStrategy = require('passport-http').BasicStrategy const AnonymousStrategy = require('passport-anonymous') -const wlogger = require('../util/winston-logging') +// const wlogger = require('../util/winston-logging') // Used for debugging and iterrogating JS objects. const util = require('util') @@ -33,9 +33,9 @@ util.inspect.defaultOptions = { depth: 1 } // let _this // Set default rate limit value for testing -const PRO_PASSES = process.env.PRO_PASS ? process.env.PRO_PASS : 'BITBOX' +// const PRO_PASSES = process.env.PRO_PASS ? process.env.PRO_PASS : 'BITBOX' // Convert the pro-tier password string into an array split by ':'. -const PRO_PASS = PRO_PASSES.split(':') +// const PRO_PASS = PRO_PASSES.split(':') // wlogger.verbose(`PRO_PASS set to: ${PRO_PASS}`) From 86bf504b782ceef4ad8c7c5c09b268805ef44fdd Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Thu, 1 Oct 2020 17:34:36 -0700 Subject: [PATCH 17/22] Fixing unit tests --- src/middleware/route-ratelimit.js | 4 +- test/v3/rate-limits.js | 70 +++++++++++++++---------------- 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index 2576500..7b8c815 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -192,13 +192,13 @@ class RateLimits { if (jwtInfo.id) { // SLP indexer routes if (level40Routes.includes(resource)) { - if (apiLevel >= 40) retVal = 1 + if (apiLevel >= 40) retVal = 10 // else if (apiLevel >= 10) retVal = 10 else retVal = 100 // Normal indexer routes } else if (level30Routes.includes(resource)) { - if (apiLevel >= 30) retVal = 1 + if (apiLevel >= 30) retVal = 10 else retVal = 100 // Full node tier diff --git a/test/v3/rate-limits.js b/test/v3/rate-limits.js index 02a1530..e47423d 100644 --- a/test/v3/rate-limits.js +++ b/test/v3/rate-limits.js @@ -105,14 +105,14 @@ describe('#route-ratelimits & jwt-auth', () => { }) describe('#calcPoints', () => { - it('should return 30 points for anonymous user', () => { + it('should return 300 points for anonymous user', () => { const result = rateLimits.calcPoints() // console.log(`result: ${result}`) - assert.equal(result, 30) + assert.equal(result, 300) }) - it('should return 10 points for free tier requesting full node access', () => { + it('should return 100 points for free tier requesting full node access', () => { const jwtInfo = { apiLevel: 10, resource: 'blockchain', @@ -120,10 +120,10 @@ describe('#route-ratelimits & jwt-auth', () => { } const result = rateLimits.calcPoints(jwtInfo) - assert.equal(result, 10) + assert.equal(result, 100) }) - it('should return 10 points for free tier requesting indexer access', () => { + it('should return 100 points for free tier requesting indexer access', () => { const jwtInfo = { apiLevel: 10, resource: 'blockbook', @@ -131,10 +131,10 @@ describe('#route-ratelimits & jwt-auth', () => { } const result = rateLimits.calcPoints(jwtInfo) - assert.equal(result, 10) + assert.equal(result, 100) }) - it('should return 10 points for free tier requesting SLPDB access', () => { + it('should return 100 points for free tier requesting SLPDB access', () => { const jwtInfo = { apiLevel: 10, resource: 'slp', @@ -142,10 +142,10 @@ describe('#route-ratelimits & jwt-auth', () => { } const result = rateLimits.calcPoints(jwtInfo) - assert.equal(result, 10) + assert.equal(result, 100) }) - it('should return 1 point for full node tier requesting full node access', () => { + it('should return 10 points for full node tier requesting full node access', () => { const jwtInfo = { apiLevel: 20, resource: 'blockchain', @@ -153,10 +153,10 @@ describe('#route-ratelimits & jwt-auth', () => { } const result = rateLimits.calcPoints(jwtInfo) - assert.equal(result, 1) + assert.equal(result, 10) }) - it('should return 10 points for full-node tier requesting indexer access', () => { + it('should return 100 points for full-node tier requesting indexer access', () => { const jwtInfo = { apiLevel: 20, resource: 'blockbook', @@ -164,10 +164,10 @@ describe('#route-ratelimits & jwt-auth', () => { } const result = rateLimits.calcPoints(jwtInfo) - assert.equal(result, 10) + assert.equal(result, 100) }) - it('should return 10 points for full node tier requesting SLPDB access', () => { + it('should return 100 points for full node tier requesting SLPDB access', () => { const jwtInfo = { apiLevel: 20, resource: 'slp', @@ -175,10 +175,10 @@ describe('#route-ratelimits & jwt-auth', () => { } const result = rateLimits.calcPoints(jwtInfo) - assert.equal(result, 10) + assert.equal(result, 100) }) - it('should return 1 point for indexer tier requesting full node access', () => { + it('should return 10 point for indexer tier requesting full node access', () => { const jwtInfo = { apiLevel: 30, resource: 'blockchain', @@ -186,32 +186,32 @@ describe('#route-ratelimits & jwt-auth', () => { } const result = rateLimits.calcPoints(jwtInfo) - assert.equal(result, 1) + assert.equal(result, 10) }) - it('should return 1 points for indexer tier requesting indexer access', () => { + it('should return 10 points for indexer tier requesting indexer access', () => { const jwtInfo = { apiLevel: 30, resource: 'blockbook', id: '5e3a0415eb29a962da2708b4' } - const result = rateLimits.calcPoints(jwtInfo) - assert.equal(result, 1) - }) - - it('should return 10 points for indexer tier requesting SLPDB access', () => { - const jwtInfo = { - apiLevel: 30, - resource: 'slp', - id: '5e3a0415eb29a962da2708b4' - } - const result = rateLimits.calcPoints(jwtInfo) assert.equal(result, 10) }) - it('should return 1 point for SLP tier requesting full node access', () => { + it('should return 100 points for indexer tier requesting SLPDB access', () => { + const jwtInfo = { + apiLevel: 30, + resource: 'slp', + id: '5e3a0415eb29a962da2708b4' + } + + const result = rateLimits.calcPoints(jwtInfo) + assert.equal(result, 100) + }) + + it('should return 10 point for SLP tier requesting full node access', () => { const jwtInfo = { apiLevel: 40, resource: 'blockchain', @@ -219,10 +219,10 @@ describe('#route-ratelimits & jwt-auth', () => { } const result = rateLimits.calcPoints(jwtInfo) - assert.equal(result, 1) + assert.equal(result, 10) }) - it('should return 1 points for SLP tier requesting indexer access', () => { + it('should return 10 points for SLP tier requesting indexer access', () => { const jwtInfo = { apiLevel: 40, resource: 'blockbook', @@ -230,10 +230,10 @@ describe('#route-ratelimits & jwt-auth', () => { } const result = rateLimits.calcPoints(jwtInfo) - assert.equal(result, 1) + assert.equal(result, 10) }) - it('should return 1 points for SLP tier requesting SLPDB access', () => { + it('should return 10 points for SLP tier requesting SLPDB access', () => { const jwtInfo = { apiLevel: 40, resource: 'slp', @@ -241,7 +241,7 @@ describe('#route-ratelimits & jwt-auth', () => { } const result = rateLimits.calcPoints(jwtInfo) - assert.equal(result, 1) + assert.equal(result, 10) }) }) @@ -442,7 +442,7 @@ describe('#route-ratelimits & jwt-auth', () => { // Issues with token secret should treat incoming requests as anonymous // calls with 30 points or 3 RPM. - assert.equal(res.locals.pointsToConsume, 30) + assert.equal(res.locals.pointsToConsume, 300) }) }) }) From 41a3cd91fcdd622e15d82ca28a290d4a9ea4505c Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Thu, 1 Oct 2020 20:20:28 -0700 Subject: [PATCH 18/22] fix(hydrateUtxos): Using local calls to the REST API --- src/routes/v3/slp.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index 3a7ef33..accd8c8 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -13,8 +13,11 @@ const Slpdb = require('./services/slpdb') // const strftime = require('strftime') const wlogger = require('../../util/winston-logging') +const LOCAL_RESTURL = process.env.LOCAL_RESTURL + ? process.env.LOCAL_RESTURL + : 'https://api.fullstack.cash/v3/' const BCHJS = require('@psf/bch-js') -const bchjs = new BCHJS() +const bchjs = new BCHJS({ restURL: LOCAL_RESTURL }) // Used to convert error messages to strings, to safely pass to users. const util = require('util') From b4823be86ae2d2c55f84b89cdb14220c5a3d5853 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Thu, 1 Oct 2020 20:46:49 -0700 Subject: [PATCH 19/22] fix(hydrateUtxos): Adding localhost to max rate limit --- src/middleware/route-ratelimit.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index 7b8c815..2f6dfad 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -136,7 +136,10 @@ class RateLimits { } // Apply paid-access rate limits based on key/IP - if (key.toString().indexOf('172.17.') > -1) { + if ( + key.toString().indexOf('172.17.') > -1 || + key.toString.indexOf('::ffff:127.0.0.1') > -1 + ) { pointsToConsume = 1 res.locals.pointsToConsume = pointsToConsume // Feedback for tests. } From c664758654bc94d136b93062f976bf2e575490b9 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Thu, 1 Oct 2020 20:51:01 -0700 Subject: [PATCH 20/22] Fixing bug --- src/middleware/route-ratelimit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index 2f6dfad..4ca8510 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -138,7 +138,7 @@ class RateLimits { // Apply paid-access rate limits based on key/IP if ( key.toString().indexOf('172.17.') > -1 || - key.toString.indexOf('::ffff:127.0.0.1') > -1 + key.toString().indexOf('::ffff:127.0.0.1') > -1 ) { pointsToConsume = 1 res.locals.pointsToConsume = pointsToConsume // Feedback for tests. From 6b2588c487c9974ade74edbf94a4f23dec239999 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Fri, 2 Oct 2020 13:33:35 -0700 Subject: [PATCH 21/22] fix(electrumx broadcast): Added body variable. Curl example works now --- src/routes/v3/electrumx.js | 4 ++-- test/v3/a01-electrumx.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/routes/v3/electrumx.js b/src/routes/v3/electrumx.js index 0c6ca7b..43f2bb7 100644 --- a/src/routes/v3/electrumx.js +++ b/src/routes/v3/electrumx.js @@ -532,13 +532,13 @@ class Electrum { * @apiDescription Broadcast a raw transaction and return the transaction ID on success or error on failure. * * @apiExample Example usage: - * curl -X POST "https://api.fullstack.cash/v3/electrumx/tx/broadcast" -H "accept: application/json" -H "Content-Type: text/plain" -d '020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000' + * curl -X POST "https://api.fullstack.cash/v3/electrumx/tx/broadcast" -H "accept: application/json" -H "Content-Type: application/json" -d '{"txHex":"020000000265d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667010000006441dd1dd72770cadede1a7fd0363574846c48468a398ddfa41a9677c74cac8d2652b682743725a3b08c6c2021a629011e11a264d9036e9d5311e35b5f4937ca7b4e4121020797d8fd4d2fa6fd7cdeabe2526bfea2b90525d6e8ad506ec4ee3c53885aa309ffffffff65d13ef402840c8a51f39779afb7ae4d49e4b0a3c24a3d0e7742038f2c679667000000006441347d7f218c11c04487c1ad8baac28928fb10e5054cd4494b94d078cfa04ccf68e064fb188127ff656c0b98e9ce87f036d183925d0d0860605877d61e90375f774121028a53f95eb631b460854fc836b2e5d31cad16364b4dc3d970babfbdcc3f2e4954ffffffff035ac355000000000017a914189ce02e332548f4804bac65cba68202c9dbf822878dfd0800000000001976a914285bb350881b21ac89724c6fb6dc914d096cd53b88acf9ef3100000000001976a91445f1f1c4a9b9419a5088a3e9c24a293d7a150e6488ac00000000"}' * */ // POST handler for broadcasting a single transaction async broadcastTransaction (req, res, next) { try { - const txHex = req.body + const txHex = req.body.txHex if (typeof txHex !== 'string') { res.status(400) diff --git a/test/v3/a01-electrumx.js b/test/v3/a01-electrumx.js index db5936a..0b45b88 100644 --- a/test/v3/a01-electrumx.js +++ b/test/v3/a01-electrumx.js @@ -957,7 +957,7 @@ describe('#ElectrumX Router', () => { }) it('should throw an error object for invalid formatted transaction', async () => { - req.body = mockData.txDetails.hex.substring(10) + req.body.txHex = mockData.txDetails.hex.substring(10) stubMethodForUnitTests( electrumxRoute, @@ -974,7 +974,7 @@ describe('#ElectrumX Router', () => { // We cannot send an actual broadcast transaction to mainnet if (process.env.TEST !== 'unit') return this.skip() - req.body = mockData.txDetails.hex + req.body.txHex = mockData.txDetails.hex stubMethodForUnitTests( electrumxRoute, From efd669e5fe5fe394dc9b97f5696058bde8b9894f Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Tue, 6 Oct 2020 13:15:53 -0700 Subject: [PATCH 22/22] fix(hydrateUtxos): Adding support for a corner case --- package.json | 2 +- src/routes/v3/slp.js | 8 ++++++++ test/v3/slp.js | 28 ++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 38b7c23..ae22554 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "test": "npm run lint && npm run test-v3", "lint": "standard --env mocha --fix", "test-v3": "export NETWORK=mainnet && nyc --reporter=text mocha --timeout 60000 test/v3/", - "test:temp": "export NETWORK=mainnet && mocha --timeout 25000 test/v3/util.js", + "test:temp": "export NETWORK=mainnet && mocha --timeout 25000 test/v3/slp.js", "test:integration": "mocha test/v3/integration", "test:integration:slpdb": "mocha --timeout 25000 test/v3/integration/slp*.js", "coverage": "nyc report --reporter=text-lcov | coveralls", diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index accd8c8..8452722 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -1570,6 +1570,13 @@ class Slp { }) } + if (!utxos[0].utxos) { + res.status(422) + return res.json({ + error: 'Each element in array should have a utxos property' + }) + } + // Loop through each address and query the UTXOs for that element. for (let i = 0; i < utxos.length; i++) { const theseUtxos = utxos[i].utxos @@ -1586,6 +1593,7 @@ class Slp { return res.json({ slpUtxos: utxos }) } catch (err) { wlogger.error('Error in slp.js/hydrateUtxos().', err) + console.error('Error in slp.js/hydrateUtxos().', err) // Decode the error message. const { msg, status } = routeUtils.decodeError(err) diff --git a/test/v3/slp.js b/test/v3/slp.js index 4c1d517..47d5537 100644 --- a/test/v3/slp.js +++ b/test/v3/slp.js @@ -1034,6 +1034,34 @@ describe('#SLP', () => { assert.property(result.slpUtxos[0].utxos[1], 'tokenType') assert.property(result.slpUtxos[0].utxos[1], 'tokenQty') }) + + it('should throw error for missing properties', async () => { + const utxos = [ + { + height: 639443, + tx_hash: + '30707fffb9b295a06a68d217f49c198e9e1dbe1edc3874a0928ca1905f1709df', + tx_pos: 0, + value: 6000 + }, + { + height: 639443, + tx_hash: + '8962566e413501224d178a02effc89be5ac0d8e4195f617415d443dc4c38fe50', + tx_pos: 1, + value: 546 + } + ] + + req.body.utxos = utxos + const result = await slpRoute.hydrateUtxos(req, res) + + assert.hasAllKeys(result, ['error']) + assert.include( + result.error, + 'Each element in array should have a utxos property' + ) + }) }) })