Compare commits

...
94 Commits
Author SHA1 Message Date
Chris Troutner a260f7506a Merge pull request #69 from Permissionless-Software-Foundation/ct-unstable
fix(whitelist): Adusting the domain names on the whitelist
2020-12-03 14:19:45 -08:00
Chris Troutner 9684441d92 fix(whitelist): Adusting the domain names on the whitelist 2020-12-03 14:17:28 -08:00
Chris Troutner e793c2a8a9 Merge pull request #68 from Permissionless-Software-Foundation/ct-unstable
fix(txDetails): Adding additional metadata to GET slp/txDetails
2020-11-29 17:59:10 -08:00
Chris Troutner 1bf8e59097 fix(txDetails): Adding additional metadata to GET slp/txDetails 2020-11-29 17:56:22 -08:00
Chris Troutner 105ecd6305 Merge pull request #67 from josephroyking/psf-add-inputs-to-txDetails
Psf add inputs to tx details
2020-11-29 17:51:13 -08:00
josephroyking 314040453b Support unconfirmed slp txs 2020-11-25 15:31:59 -08:00
josephroyking cbcf5c1d2b Add tx input details to slp/txDetails endpoint 2020-11-25 10:56:10 -08:00
Chris Troutner e881b68ca5 Merge pull request #65 from Permissionless-Software-Foundation/ct-unstable
ix(electrumx): Fixed connect/disconnect bug that was preventing tests
2020-11-21 11:39:05 -08:00
Chris Troutner f54c69a664 Fixing test typo 2020-11-21 11:35:01 -08:00
Chris Troutner 319281c096 fix(electrumx): Fixed connect/disconnect bug that was preventing tests 2020-11-21 11:29:18 -08:00
Chris Troutner 7dee8c0604 Merge branch 'master' into ct-unstable 2020-11-21 11:15:41 -08:00
Chris Troutner efbcbc5deb linting 2020-11-21 11:15:33 -08:00
Chris Troutner c742518612 Merge pull request #64 from Permissionless-Software-Foundation/ct-unstable
fix(electrumx): Automatically reconnecting when Electrumx disconnects
2020-11-20 20:53:40 -08:00
Chris Troutner 6a0ed46413 fix(electrumx): Automatically reconnecting when Electrumx disconnects 2020-11-20 20:52:22 -08:00
Chris Troutner 5ea71bc89b Updating default Docker run script 2020-11-20 08:36:34 -08:00
Chris Troutner 84701e90dc Merge pull request #63 from Permissionless-Software-Foundation/ct-unstable
fix(price): Fixing bug in new price endpoint
2020-11-17 14:29:22 -08:00
Chris Troutner 34ed5ddc4c fix(price): Fixing bug in new price endpoint 2020-11-17 14:27:42 -08:00
Chris Troutner 9a8cd4b664 Merge pull request #62 from Permissionless-Software-Foundation/ct-unstable
fix(price): Adding Coinex price feed for BCHA
2020-11-17 14:00:37 -08:00
Chris Troutner 2a0b06ca96 fix(price): Adding Coinex price feed for BCHA 2020-11-17 13:48:10 -08:00
Chris Troutner 9c70fc5007 Merge pull request #61 from Permissionless-Software-Foundation/ct-unstable
fix(whitelist): Adding splitbch.com to the whitelist
2020-11-14 17:39:41 -08:00
Chris Troutner b24b659124 fix(whitelist): Adding splitbch.com to the whitelist 2020-11-14 17:36:38 -08:00
Chris Troutner 7ad5acef34 Merge pull request #60 from Permissionless-Software-Foundation/ct-unstable
fix(rate limits): Setting anonymous rate limits to a constant
2020-11-12 09:11:48 -08:00
Chris Troutner 304a3f1d5a fix(rate limits): Setting anonymous rate limits to a constant 2020-11-12 09:10:35 -08:00
Chris Troutner 86a58e9ff3 Merge pull request #59 from Permissionless-Software-Foundation/ct-unstable
fix(auth): Fixing typo that was breaking basic authentication
2020-11-11 19:58:11 -08:00
Chris Troutner d7d722b27b Merge branch 'master' into ct-unstable 2020-11-11 19:55:58 -08:00
Chris Troutner d439165dd2 fix(auth): Fixing typo that was breaking basic authentication 2020-11-11 19:55:15 -08:00
Chris Troutner 65356051f8 Merge pull request #58 from Permissionless-Software-Foundation/ct-unstable
fix(rate limits): Adding local server back to whitelist
2020-11-11 19:16:07 -08:00
Chris Troutner 9728b64edb fix(rate limits): Adding local server back to whitelist 2020-11-11 18:59:48 -08:00
Chris Troutner 7e71d355ba Merge pull request #57 from Permissionless-Software-Foundation/ct-unstable
Basic Authentication
2020-11-11 17:53:38 -08:00
Chris Troutner fe9f40e847 linting 2020-11-11 17:52:19 -08:00
Chris Troutner 28f0a1de56 feat(Basic Authentication): Adding back in for controlling access 2020-11-11 14:56:24 -08:00
Chris Troutner b8589a54aa Trying to reinstate basic auth 2020-11-11 07:48:40 -08:00
Chris Troutner 0d05803462 Update README 2020-11-08 19:19:21 -08:00
Chris Troutner 69d125ad24 fixing README 2020-11-08 17:30:55 -08:00
Chris Troutner 7631a25f4b Merge pull request #56 from Permissionless-Software-Foundation/ct-unstable
Removing Blockbook documentation
2020-11-08 17:26:39 -08:00
Chris Troutner f41f9d33c8 feat(blockbook): Removing Blockbook documentation 2020-11-08 17:25:05 -08:00
Chris Troutner 3475255444 Updating diagram 2020-11-08 17:22:57 -08:00
Chris Troutner bcc5c95bd6 Updating README 2020-11-08 17:20:58 -08:00
Chris Troutner 15fe66bdf3 Merge branch 'master' into ct-unstable 2020-11-08 17:19:43 -08:00
Chris Troutner 8ce0209e9a Updating README 2020-11-08 17:19:30 -08:00
Chris Troutner c7c19d1c2b Merge pull request #55 from Permissionless-Software-Foundation/ct-unstable
Updating README
2020-11-08 17:17:15 -08:00
Chris Troutner f36146ef24 Updating README 2020-11-08 17:15:53 -08:00
Chris Troutner edae956742 Updating README 2020-11-08 17:13:03 -08:00
Chris Troutner 3f2890661c Updating README 2020-11-08 17:04:17 -08:00
Chris Troutner 46c9e00fe1 Merge pull request #54 from Permissionless-Software-Foundation/ct-unstable
Updating deployment for bchn
2020-11-08 13:42:48 -08:00
Chris Troutner a1c19e8e0a Merge branch 'master' into ct-unstable 2020-11-08 13:40:08 -08:00
Chris Troutner fea8546bd2 fix(deployment): Updating deployment script 2020-11-08 13:39:10 -08:00
Chris Troutner c6d98380b1 Updating gitignore 2020-11-08 12:54:28 -08:00
Chris Troutner 534a652ffe Merge pull request #53 from Permissionless-Software-Foundation/ct-unstable
fix(encryption): Refactored encryption lib to use Fulcrum instead of Blockbook
2020-11-08 12:36:35 -08:00
Chris Troutner a5cee9636e Updating tests for bchn 2020-11-08 12:35:18 -08:00
Chris Troutner 555de52844 fix(encryption): Refactored encryption lib to use Fulcrum instead of Blockbook 2020-11-08 12:26:52 -08:00
Chris Troutner 63e8078681 Merge pull request #52 from Permissionless-Software-Foundation/ct-unstable
feat(rate limits): Bumping anonymous rate limits to 20 RPM
2020-11-08 08:18:36 -08:00
Chris Troutner 821f908fdb feat(rate limits): Bumping anonymous rate limits to 20 RPM 2020-11-08 08:07:16 -08:00
Chris Troutner 9d5a0d9520 Merge pull request #51 from Permissionless-Software-Foundation/ct-unstable
Ct unstable
2020-11-08 07:20:46 -08:00
Chris Troutner f146504ed0 Merge branch 'master' into ct-unstable 2020-11-08 07:17:40 -08:00
Chris Troutner 03a4eb1b72 Debugging deployment from CI 2020-11-08 07:17:30 -08:00
Chris Troutner 6286b4967b Merge pull request #50 from Permissionless-Software-Foundation/ct-unstable
Ct unstable
2020-11-08 07:10:36 -08:00
Chris Troutner 6bf86b29f8 Merge branch 'master' into ct-unstable 2020-11-08 07:09:18 -08:00
Chris Troutner 0b180e02a4 Debugging deployment from CI 2020-11-08 07:09:08 -08:00
Chris Troutner a2267c9590 Merge pull request #49 from Permissionless-Software-Foundation/ct-unstable
Ct unstable
2020-11-07 22:01:41 -08:00
Chris Troutner ed83f5eb3f Merge branch 'master' into ct-unstable 2020-11-07 21:59:36 -08:00
Chris Troutner 91e5423dd2 Debugging deployment from CI 2020-11-07 21:59:20 -08:00
Chris Troutner de4cb8762a Merge pull request #48 from Permissionless-Software-Foundation/ct-unstable
Ct unstable
2020-11-07 21:52:34 -08:00
Chris Troutner 284e9a9ee7 Merge branch 'master' into ct-unstable 2020-11-07 21:50:47 -08:00
Chris Troutner ec2fb8e24f testing ci 2020-11-07 21:50:34 -08:00
Chris Troutner 07fc05fa12 Merge pull request #47 from Permissionless-Software-Foundation/ct-unstable
testing ci
2020-11-07 21:37:12 -08:00
Chris Troutner 2bfad5d261 testing ci 2020-11-07 21:34:04 -08:00
Chris Troutner efe4cad08b Merge pull request #46 from Permissionless-Software-Foundation/ct-unstable
testing ci
2020-11-07 21:27:30 -08:00
Chris Troutner b704707ded Merge branch 'master' into ct-unstable 2020-11-07 21:26:00 -08:00
Chris Troutner 6c8a156f37 testing ci 2020-11-07 21:24:45 -08:00
Chris Troutner 19d9153629 Merge pull request #45 from Permissionless-Software-Foundation/ct-unstable
Switching CI from Travis to Jenkins
2020-11-07 21:10:20 -08:00
Chris Troutner 840e92e54c testing ci 2020-11-07 21:00:05 -08:00
Chris Troutner 8e4592826f Testing CI 2020-11-07 20:47:38 -08:00
Chris Troutner c4085fb929 Updating deploy script for Jenkins 2020-11-07 20:40:06 -08:00
Chris Troutner 00e1840ca2 Updating gitignore 2020-11-07 19:42:58 -08:00
Chris Troutner 7e9a9bc635 update to gitignore 2020-11-07 18:34:38 -08:00
Chris Troutner 41163096e3 Merge pull request #44 from Permissionless-Software-Foundation/ct-unstable
fix(raw transactions): Updating for change in output of abc full node
2020-10-31 10:14:19 -07:00
Chris Troutner 1b3e1cc293 fix(raw transactions): Updating for change in output of abc full node 2020-10-31 10:11:44 -07:00
Chris Troutner 72460369f5 Merge pull request #43 from Permissionless-Software-Foundation/ct-unstable
Adding price endpoint
2020-10-19 17:56:06 -07:00
Chris Troutner 5459fc8929 feat(price): Adding price endpoint 2020-10-19 17:50:49 -07:00
Chris Troutner b4571cf71c Update to gitignore 2020-10-16 22:22:55 -07:00
Chris Troutner 54a472af5f Fixing curl example for hydrateUtxos() 2020-10-12 11:03:46 -07:00
Chris Troutner ebbf12c394 Merge pull request #42 from Permissionless-Software-Foundation/ct-unstable
fix(bch-js): Updated to v 3.7.1
2020-10-11 18:09:42 -07:00
Chris Troutner eeb63ec373 fix(bch-js): Updated to v 3.7.1 2020-10-11 18:04:30 -07:00
Chris Troutner 8244fbbef5 removing a comment 2020-10-11 17:02:17 -07:00
Chris Troutner 43dfea7cbe Merge pull request #41 from Permissionless-Software-Foundation/ct-unstable
validateTxid2 - slp-validate based
2020-10-11 10:09:18 -07:00
Chris Troutner a2c30aee6d fix(validateTxid2): Added unit tests 2020-10-11 10:03:24 -07:00
Chris Troutner 1959b20115 Removed integrated slp-validate dependencies 2020-10-11 09:44:28 -07:00
Chris Troutner e399e05022 fix(validateTxid2): Final form. Updated integration tests 2020-10-11 09:10:35 -07:00
Chris Troutner 2d04169329 Working on killing promise that takes too long 2020-10-10 12:19:27 -07:00
Chris Troutner 5619c2fbed Added a couple integration tests around validate2Single() 2020-10-10 12:03:02 -07:00
Chris Troutner 388a3ee53b fix(validate2Single): Added alternative SLP TXID validator 2020-10-10 09:03:14 -07:00
Chris Troutner 8aff89dead Merge pull request #40 from Permissionless-Software-Foundation/ct-unstable
fix(hydrateUtxos): Adding support for a corner case
2020-10-06 13:23:02 -07:00
Chris Troutner efd669e5fe fix(hydrateUtxos): Adding support for a corner case 2020-10-06 13:15:53 -07:00
31 changed files with 1944 additions and 611 deletions
+7
View File
@@ -20,6 +20,13 @@ start-free-main.sh
start-local-mainnet.sh.save start-local-mainnet.sh.save
start-local-testnet.sh.save start-local-testnet.sh.save
temp.sh temp.sh
test-fullstack-test.sh
start-sweet-main.sh
test-sweet-main.sh
test-fullstack-abc.sh
test-fullstack-bchn.sh
start-fullstack-abc.sh
start-fullstack-bchn.sh
coverage coverage
start-my-infra start-my-infra
+20 -25
View File
@@ -1,19 +1,12 @@
# bch-api # bch-api
This is a node.js JavaScript REST API server using Express.js. The purpose of this code is to create a REST API server that provides a common interface for working with a Bitcoin Cash full node and various indexers. See [this article](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer) to learn about the 'Cash Stack'. Visit [FullStack.cash](https://fullstack.cash), sign up for a free account, and use this REST API right away with the [bch-js](https://github.com/Permissionless-Software-Foundation/bch-js) JavaScript library.
[![Build Status](https://travis-ci.org/christroutner/bch-api.svg?branch=master)](https://travis-ci.org/christroutner/bch-api) This repository is intended to be paired with [bch-js](https://github.com/Permissionless-Software-Foundation/bch-js), an npm JavaScript library for building Bitcoin Cash apps.
This is a fork and alternative implementation of ![Cash Stack Network Diagram](./generic-network-diagram.png)
the [rest.bitcoin.com](https://github.com/Bitcoin-com/rest.bitcoin.com) repository.
The purpose of this code is to create a REST API server that provides a common
interface for working with a Bitcoin Cash full node and various indexers. See [this article](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer) to learn about the 'Cash Stack'. Visit [FullStack.cash](https://fullstack.cash), sign up for a free account, and use this REST API right away with the [bch-js](https://github.com/christroutner/bch-js) JavaScript library.
This repository is intended to be paired with [bch-js](https://github.com/christroutner/bch-js), Both bch-api and bch-js are part of the 'Cash Stack' [full stack of BCH software](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer).
an npm JavaScript library, and an alternative implementation
of [BITBOX SDK](https://github.com/Bitcoin-com/bitbox-sdk).
Both bch-api and bch-js are part of the
[full stack of BCH software](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer).
- [API Documentation](https://fullstack.cash/documentation) - [API Documentation](https://fullstack.cash/documentation)
- [Example Code](https://fullstack.cash/examples) - [Example Code](https://fullstack.cash/examples)
@@ -24,32 +17,29 @@ Have questions? Need help? Join our community support:
## Features ## Features
The following features set this repository apart from rest.bitcoin.com: The following features set this repository apart from rest.bitcoin.com:
- Address balance and UTXO queries use the [Blockbook](https://github.com/trezor/blockbook)
indexer instead of Insight.
- Fine grain access is controlled with a JWT token using - Fine grain access is controlled with a JWT token using
[this back end auth server](https://github.com/Permissionless-Software-Foundation/jwt-bch-api) and [this front end](https://github.com/Permissionless-Software-Foundation/jwt-bch-frontend). [this back end auth server](https://github.com/Permissionless-Software-Foundation/jwt-bch-api) and [this front end](https://github.com/Permissionless-Software-Foundation/jwt-bch-frontend). Can be used to monetize access to the REST API.
- Default rate limits are set to 3 RPM for anonymous connections, 10 RPM for [free accounts](https://fullstack.cash/pricing), up to 100 RPM if a full-access JWT token is used.
- Typescript removed and ES8 JavaScript used instead. - Typescript removed and ES8 JavaScript used instead.
- npm audit run on all dependencies. - npm audit run on all dependencies.
- [Greenkeeper](https://greenkeeper.io/) implemented for automatic dependency management
and security updates.
## Live Demo ## Live Demo
You can test a live demo of the REST API by running the You can test a live demo of the REST API by running the
[bch-js examples](https://github.com/Permissionless-Software-Foundation/bch-js-examples). [bch-js examples](https://github.com/Permissionless-Software-Foundation/bch-js-examples).
Rate limits are 3 requests per minute, but you can increase them to 10 with a [free account](https://fullstack.cash/pricing). Rate limits are 20 requests per minute, but you can increase them to 100 with a [paid account](https://fullstack.cash/pricing).
This is fast enough to try out the examples You can bootstrap your own REST API server by downloading and installing the infrastructure listed on the [CashStrap](https://fullstack.cash/cashstrap) page.
but these servers are not intended as a freemium service. You can run your own
REST server by purchasing the hard drive at [bchjs.cash](https://bchjs.cash).
- Mainnet REST API server: https://api.fullstack.cash/v3/ ### Configure bch-js
- Testnet REST API server: https://tapi.fullstack.cash/v3/ The live servers can be used by [bch-js](https://github.com/Permissionless-Software-Foundation/bch-js) by settings the `restURL` config property to one of these servers:
- BCHN Mainnet REST API server: https://bchn.fullstack.cash/v3/
- ABC Mainnet REST API server: https://abc.fullstack.cash/v3/
- Testnet3 REST API server: https://testnet3.fullstack.cash/v3/
- Check server status: https://metrics.fullstack.cash - Check server status: https://metrics.fullstack.cash
## Installation ## Installation
There are two installation paths, depending if you want a *development* or There are two installation paths, depending if you want a *development* or
*production* environment. You'll also need to set up the underlying infrastructure *production* environment. You'll also need to set up the underlying infrastructure
described [this page](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer). described on [this page](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer).
This code targets the Ubuntu 18.04 LTS Linux OS. This code targets the Ubuntu 18.04 LTS Linux OS.
@@ -58,7 +48,7 @@ This is a standard node.js project. The installation is as follows:
- Clone this repository: - Clone this repository:
`git clone https://github.com/christroutner/bch-api && cd bch-api` `git clone https://github.com/Permissionless-Software-Foundation/bch-api && cd bch-api`
- Install dependencies: - Install dependencies:
@@ -93,5 +83,10 @@ installation.
Have questions? Need help? Join our community support Have questions? Need help? Join our community support
[Telegram channel](https://t.me/bch_js_toolkit) [Telegram channel](https://t.me/bch_js_toolkit)
## IPFS
Copies of this repository will occasionally be uploaded and hosted on [IPFS](https://ipfs.io):
- v1.15.4: QmSAerFmDJm6KQp2HC8jsRR5MdL5X39hoUsJYvWNDvrD8m
## License ## License
[MIT](./LICENSE.md) [MIT](./LICENSE.md)
+3 -1
View File
@@ -3,7 +3,9 @@
*/ */
const config = { const config = {
apiTokenSecret: process.env.TOKENSECRET ? process.env.TOKENSECRET : 'secret-jwt-token' apiTokenSecret: process.env.TOKENSECRET
? process.env.TOKENSECRET
: 'secret-jwt-token'
} }
module.exports = config module.exports = config
+19 -9
View File
@@ -1,17 +1,27 @@
#!/bin/bash
# Triggers a webhook to update the staging server at staging.fullstack.cash. # Triggers a webhook to update the staging server at staging.fullstack.cash.
#!/bin/bash
#echo $DEPLOY_SECRET #echo $DEPLOY_SECRET
echo "Deploy to production...." # This if statement will only deploy on Jenkins when its testing the master branch.
echo "-->$GIT_BRANCH<--"
if [[ $GIT_BRANCH =~ "master" ]]
then
export DATA="{\"ref\":\"$DEPLOY_SECRET\"}" echo "Deploying to production...."
#echo $DATA export DATA="{\"ref\":\"$DEPLOY_SECRET\"}"
curl -X POST http://fullstack.cash:9000/hooks/bch-api-mainnet -H "Content-Type: application/json" -d $DATA #echo $DATA
curl -X POST http://fullstack.cash:9000/hooks/bch-api-testnet -H "Content-Type: application/json" -d $DATA
curl -X POST http://$FREE_SERVER:9000/hooks/bch-api-testnet -H "Content-Type: application/json" -d $DATA
curl -X POST http://$FREE_SERVER:9000/hooks/bch-api-mainnet -H "Content-Type: application/json" -d $DATA
echo "...Finished deploying to production." curl -X POST http://fullstack.cash:9000/hooks/bch-api-mainnet -H "Content-Type: application/json" -d $DATA
curl -X POST http://fullstack.cash:9000/hooks/bch-api-testnet -H "Content-Type: application/json" -d $DATA
#curl -X POST http://$FREE_SERVER:9000/hooks/bch-api-testnet -H "Content-Type: application/json" -d $DATA
#curl -X POST http://$FREE_SERVER:9000/hooks/bch-api-mainnet -H "Content-Type: application/json" -d $DATA
curl -X POST http://$BCHN_BCHAPI:9000/hooks/bch-api-bchn -H "Content-Type: application/json" -d $DATA
echo "...Finished deploying to production."
else
echo "Not master, so not deploying."
fi
+11 -1
View File
@@ -17,11 +17,21 @@ export BLOCKBOOK_URL=https://172.17.0.1:9131/
# Allow node.js to make network calls to https using self-signed certificate. # Allow node.js to make network calls to https using self-signed certificate.
export NODE_TLS_REJECT_UNAUTHORIZED=0 export NODE_TLS_REJECT_UNAUTHORIZED=0
# Redis DB # Mainnet Fulcrum / ElectrumX
export FULCRUM_URL=172.17.0.1
export FULCRUM_PORT=50002
# Redis DB - Used for rate limiting
export REDIS_PORT=6379 export REDIS_PORT=6379
export REDIS_HOST=172.17.0.1 export REDIS_HOST=172.17.0.1
# JWT Token Secret # JWT Token Secret
export TOKENSECRET=somelongsecretvalue export TOKENSECRET=somelongsecretvalue
# So that bch-api can call bch-js locally.
export LOCAL_RESTURL=http://127.0.0.1:3000/v3/
# slp-api alternative SLP validator.
export SLP_API_URL=http://10.0.0.5:5001/
npm start npm start
+10
View File
@@ -14,6 +14,10 @@ export BLOCKBOOK_URL=https://172.17.0.1:19131/
# Allow node.js to make network calls to https using self-signed certificate. # Allow node.js to make network calls to https using self-signed certificate.
export NODE_TLS_REJECT_UNAUTHORIZED=0 export NODE_TLS_REJECT_UNAUTHORIZED=0
# Mainnet Fulcrum / ElectrumX
export FULCRUM_URL=172.17.0.1
export FULCRUM_PORT=50002
# Redis DB # Redis DB
export REDIS_PORT=6380 export REDIS_PORT=6380
export REDIS_HOST=172.17.0.1 export REDIS_HOST=172.17.0.1
@@ -21,4 +25,10 @@ export REDIS_HOST=172.17.0.1
# JWT Token Secret # JWT Token Secret
export TOKENSECRET=somelongsecretvalue export TOKENSECRET=somelongsecretvalue
# So that bch-api can call bch-js locally.
export LOCAL_RESTURL=http://127.0.0.1:3000/v3/
# slp-api alternative SLP validator.
export SLP_API_URL=http://10.0.0.5:5001/
npm start npm start
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

+477 -139
View File
@@ -366,9 +366,9 @@
} }
}, },
"@psf/bch-js": { "@psf/bch-js": {
"version": "3.6.3", "version": "3.7.1",
"resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-3.6.3.tgz", "resolved": "https://registry.npmjs.org/@psf/bch-js/-/bch-js-3.7.1.tgz",
"integrity": "sha512-0nqmLprZU+1lh1WTnAqVHQD+bIPJIcEQ36rBNoyh4ejzWZoQ7pTMj2r2I7JGhWmxWdSQ+g4stT1OrC4qmH6jdw==", "integrity": "sha512-4r7J8fBtMuvBeO3Dme0QXcrKs+hmlpbH3Cjl/E3qFN8SzZqLyOj7Adfv/u5iCpd8DyP2K30Q0qNrAuiSSSVAWA==",
"requires": { "requires": {
"@uppy/core": "^1.10.4", "@uppy/core": "^1.10.4",
"@uppy/tus": "^1.5.12", "@uppy/tus": "^1.5.12",
@@ -905,9 +905,9 @@
"integrity": "sha512-d4YTq6SeH792+vG9OXGCfmCIoo4RteLJKU2mZCzMYNjCZ1yNfU/FVw1r7heTc7f2wNls56ABor2xSybk/X+wFA==" "integrity": "sha512-d4YTq6SeH792+vG9OXGCfmCIoo4RteLJKU2mZCzMYNjCZ1yNfU/FVw1r7heTc7f2wNls56ABor2xSybk/X+wFA=="
}, },
"@uppy/tus": { "@uppy/tus": {
"version": "1.7.6", "version": "1.7.7",
"resolved": "https://registry.npmjs.org/@uppy/tus/-/tus-1.7.6.tgz", "resolved": "https://registry.npmjs.org/@uppy/tus/-/tus-1.7.7.tgz",
"integrity": "sha512-Aetmw/EjwXuUl6XoosiFbqSk59iRuTfsJmKoWL+TI4PiE+NPVYreRao7vS1Dy072cQo7nD9MlK/tAFOZ/Lf7DQ==", "integrity": "sha512-Fb8xlEeMmhbHeb4CvWuk3vXzhCV3vxZf7D0J/gg3OjDyWnKLnzNjHyfoNvjgukhd0V0Qes+bDxeSwkk3XXrc0g==",
"requires": { "requires": {
"@uppy/companion-client": "^1.5.4", "@uppy/companion-client": "^1.5.4",
"@uppy/utils": "^3.2.3", "@uppy/utils": "^3.2.3",
@@ -1604,9 +1604,9 @@
} }
}, },
"base64-arraybuffer": { "base64-arraybuffer": {
"version": "0.1.5", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz",
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg=" "integrity": "sha1-mBjHngWbE1X5fgQooBfIOOkLqBI="
}, },
"base64-js": { "base64-js": {
"version": "1.3.1", "version": "1.3.1",
@@ -3933,40 +3933,41 @@
} }
}, },
"deep-equal": { "deep-equal": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.3.tgz", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.4.tgz",
"integrity": "sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA==", "integrity": "sha512-BUfaXrVoCfgkOQY/b09QdO9L3XNoF2XH0A3aY9IQwQL/ZjLOe8FQgCNVl1wiolhsFo8kFdO9zdPViCPbmaJA5w==",
"requires": { "requires": {
"es-abstract": "^1.17.5", "es-abstract": "^1.18.0-next.1",
"es-get-iterator": "^1.1.0", "es-get-iterator": "^1.1.0",
"is-arguments": "^1.0.4", "is-arguments": "^1.0.4",
"is-date-object": "^1.0.2", "is-date-object": "^1.0.2",
"is-regex": "^1.0.5", "is-regex": "^1.1.1",
"isarray": "^2.0.5", "isarray": "^2.0.5",
"object-is": "^1.1.2", "object-is": "^1.1.3",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.0", "object.assign": "^4.1.1",
"regexp.prototype.flags": "^1.3.0", "regexp.prototype.flags": "^1.3.0",
"side-channel": "^1.0.2", "side-channel": "^1.0.3",
"which-boxed-primitive": "^1.0.1", "which-boxed-primitive": "^1.0.1",
"which-collection": "^1.0.1", "which-collection": "^1.0.1",
"which-typed-array": "^1.1.2" "which-typed-array": "^1.1.2"
}, },
"dependencies": { "dependencies": {
"es-abstract": { "es-abstract": {
"version": "1.17.6", "version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": { "requires": {
"es-to-primitive": "^1.2.1", "es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
"has-symbols": "^1.0.1", "has-symbols": "^1.0.1",
"is-callable": "^1.2.0", "is-callable": "^1.2.2",
"is-regex": "^1.1.0", "is-negative-zero": "^2.0.0",
"object-inspect": "^1.7.0", "is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.0", "object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1", "string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1" "string.prototype.trimstart": "^1.0.1"
} }
@@ -4013,6 +4014,17 @@
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
},
"object.assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
"integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.0",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
}
} }
} }
}, },
@@ -4454,18 +4466,18 @@
} }
}, },
"engine.io-client": { "engine.io-client": {
"version": "3.4.3", "version": "3.4.4",
"resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.3.tgz", "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.4.4.tgz",
"integrity": "sha512-0NGY+9hioejTEJCaSJZfWZLk4FPI9dN+1H1C4+wj2iuFba47UgZbJzfWs4aNFajnX/qAaYKbe2lLTfEEWzCmcw==", "integrity": "sha512-iU4CRr38Fecj8HoZEnFtm2EiKGbYZcPn3cHxqNGl/tmdWRf60KhK+9vE0JeSjgnlS/0oynEfLgKbT9ALpim0sQ==",
"requires": { "requires": {
"component-emitter": "~1.3.0", "component-emitter": "~1.3.0",
"component-inherit": "0.0.3", "component-inherit": "0.0.3",
"debug": "~4.1.0", "debug": "~3.1.0",
"engine.io-parser": "~2.2.0", "engine.io-parser": "~2.2.0",
"has-cors": "1.1.0", "has-cors": "1.1.0",
"indexof": "0.0.1", "indexof": "0.0.1",
"parseqs": "0.0.5", "parseqs": "0.0.6",
"parseuri": "0.0.5", "parseuri": "0.0.6",
"ws": "~6.1.0", "ws": "~6.1.0",
"xmlhttprequest-ssl": "~1.5.4", "xmlhttprequest-ssl": "~1.5.4",
"yeast": "0.1.2" "yeast": "0.1.2"
@@ -4476,6 +4488,29 @@
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==" "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
}, },
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"parseqs": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
},
"parseuri": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
},
"ws": { "ws": {
"version": "6.1.4", "version": "6.1.4",
"resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.4.tgz",
@@ -4487,13 +4522,13 @@
} }
}, },
"engine.io-parser": { "engine.io-parser": {
"version": "2.2.0", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.1.tgz",
"integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==", "integrity": "sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg==",
"requires": { "requires": {
"after": "0.8.2", "after": "0.8.2",
"arraybuffer.slice": "~0.0.7", "arraybuffer.slice": "~0.0.7",
"base64-arraybuffer": "0.1.5", "base64-arraybuffer": "0.1.4",
"blob": "0.0.5", "blob": "0.0.5",
"has-binary2": "~1.0.2" "has-binary2": "~1.0.2"
} }
@@ -4638,19 +4673,19 @@
}, },
"dependencies": { "dependencies": {
"es-abstract": { "es-abstract": {
"version": "1.17.6", "version": "1.17.7",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
"requires": { "requires": {
"es-to-primitive": "^1.2.1", "es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
"has-symbols": "^1.0.1", "has-symbols": "^1.0.1",
"is-callable": "^1.2.0", "is-callable": "^1.2.2",
"is-regex": "^1.1.0", "is-regex": "^1.1.1",
"object-inspect": "^1.7.0", "object-inspect": "^1.8.0",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.0", "object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1", "string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1" "string.prototype.trimstart": "^1.0.1"
} }
@@ -4692,6 +4727,38 @@
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
},
"object.assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
"integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.0",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"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.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
}
}
} }
} }
}, },
@@ -6458,6 +6525,13 @@
"p-cancelable": "^1.0.0", "p-cancelable": "^1.0.0",
"to-readable-stream": "^1.0.0", "to-readable-stream": "^1.0.0",
"url-parse-lax": "^3.0.0" "url-parse-lax": "^3.0.0"
},
"dependencies": {
"p-cancelable": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
"integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw=="
}
} }
}, },
"graceful-fs": { "graceful-fs": {
@@ -7373,19 +7447,19 @@
}, },
"dependencies": { "dependencies": {
"es-abstract": { "es-abstract": {
"version": "1.17.6", "version": "1.17.7",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
"requires": { "requires": {
"es-to-primitive": "^1.2.1", "es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
"has-symbols": "^1.0.1", "has-symbols": "^1.0.1",
"is-callable": "^1.2.0", "is-callable": "^1.2.2",
"is-regex": "^1.1.0", "is-regex": "^1.1.1",
"object-inspect": "^1.7.0", "object-inspect": "^1.8.0",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.0", "object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1", "string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1" "string.prototype.trimstart": "^1.0.1"
} }
@@ -7422,6 +7496,38 @@
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
},
"object.assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
"integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.0",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"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.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
}
}
} }
} }
}, },
@@ -12854,28 +12960,29 @@
"dev": true "dev": true
}, },
"object-is": { "object-is": {
"version": "1.1.2", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.3.tgz",
"integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==", "integrity": "sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==",
"requires": { "requires": {
"define-properties": "^1.1.3", "define-properties": "^1.1.3",
"es-abstract": "^1.17.5" "es-abstract": "^1.18.0-next.1"
}, },
"dependencies": { "dependencies": {
"es-abstract": { "es-abstract": {
"version": "1.17.6", "version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": { "requires": {
"es-to-primitive": "^1.2.1", "es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
"has-symbols": "^1.0.1", "has-symbols": "^1.0.1",
"is-callable": "^1.2.0", "is-callable": "^1.2.2",
"is-regex": "^1.1.0", "is-negative-zero": "^2.0.0",
"object-inspect": "^1.7.0", "is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.0", "object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1", "string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1" "string.prototype.trimstart": "^1.0.1"
} }
@@ -12912,6 +13019,17 @@
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
},
"object.assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
"integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.0",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
}
} }
} }
}, },
@@ -13340,11 +13458,6 @@
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
}, },
"p-cancelable": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz",
"integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw=="
},
"p-each-series": { "p-each-series": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz",
@@ -14318,19 +14431,19 @@
}, },
"dependencies": { "dependencies": {
"es-abstract": { "es-abstract": {
"version": "1.17.6", "version": "1.17.7",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
"requires": { "requires": {
"es-to-primitive": "^1.2.1", "es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
"has-symbols": "^1.0.1", "has-symbols": "^1.0.1",
"is-callable": "^1.2.0", "is-callable": "^1.2.2",
"is-regex": "^1.1.0", "is-regex": "^1.1.1",
"object-inspect": "^1.7.0", "object-inspect": "^1.8.0",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.0", "object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1", "string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1" "string.prototype.trimstart": "^1.0.1"
} }
@@ -14367,6 +14480,38 @@
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
},
"object.assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
"integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.0",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"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.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
}
}
} }
} }
}, },
@@ -15131,20 +15276,20 @@
}, },
"dependencies": { "dependencies": {
"es-abstract": { "es-abstract": {
"version": "1.18.0-next.0", "version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==", "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": { "requires": {
"es-to-primitive": "^1.2.1", "es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
"has-symbols": "^1.0.1", "has-symbols": "^1.0.1",
"is-callable": "^1.2.0", "is-callable": "^1.2.2",
"is-negative-zero": "^2.0.0", "is-negative-zero": "^2.0.0",
"is-regex": "^1.1.1", "is-regex": "^1.1.1",
"object-inspect": "^1.8.0", "object-inspect": "^1.8.0",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.0", "object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1", "string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1" "string.prototype.trimstart": "^1.0.1"
} }
@@ -15181,6 +15326,17 @@
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
},
"object.assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
"integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.0",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
}
} }
} }
}, },
@@ -15418,34 +15574,13 @@
"socket.io-adapter": "~1.1.0", "socket.io-adapter": "~1.1.0",
"socket.io-client": "2.3.0", "socket.io-client": "2.3.0",
"socket.io-parser": "~3.4.0" "socket.io-parser": "~3.4.0"
}
},
"socket.io-adapter": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
"integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
},
"socket.io-client": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
"integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
"requires": {
"backo2": "1.0.2",
"base64-arraybuffer": "0.1.5",
"component-bind": "1.0.0",
"component-emitter": "1.2.1",
"debug": "~4.1.0",
"engine.io-client": "~3.4.0",
"has-binary2": "~1.0.2",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"object-component": "0.0.3",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
}, },
"dependencies": { "dependencies": {
"base64-arraybuffer": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz",
"integrity": "sha1-c5JncZI7Whl0etZmqlzUv5xunOg="
},
"isarray": { "isarray": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
@@ -15456,28 +15591,124 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}, },
"socket.io-parser": { "socket.io-client": {
"version": "3.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.0.tgz", "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.0.tgz",
"integrity": "sha512-hczmV6bDgdaEbVqhAeVMM/jfUfzuEZHsQg6eOmLgJht6G3mPKMxYm75w2+qhAQZ+4X+1+ATZ+QFKeOZD5riHng==", "integrity": "sha512-cEQQf24gET3rfhxZ2jJ5xzAOo/xhZwK+mOqtGRg5IowZsMgwvHwnf/mCRapAAkadhM26y+iydgwsXGObBB5ZdA==",
"requires": { "requires": {
"backo2": "1.0.2",
"base64-arraybuffer": "0.1.5",
"component-bind": "1.0.0",
"component-emitter": "1.2.1", "component-emitter": "1.2.1",
"debug": "~3.1.0", "debug": "~4.1.0",
"isarray": "2.0.1" "engine.io-client": "~3.4.0",
"has-binary2": "~1.0.2",
"has-cors": "1.1.0",
"indexof": "0.0.1",
"object-component": "0.0.3",
"parseqs": "0.0.5",
"parseuri": "0.0.5",
"socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
}, },
"dependencies": { "dependencies": {
"debug": { "socket.io-parser": {
"version": "3.1.0", "version": "3.3.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "integrity": "sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==",
"requires": { "requires": {
"ms": "2.0.0" "component-emitter": "~1.3.0",
"debug": "~3.1.0",
"isarray": "2.0.1"
},
"dependencies": {
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
}
} }
} }
} }
} }
} }
}, },
"socket.io-adapter": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz",
"integrity": "sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g=="
},
"socket.io-client": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.3.1.tgz",
"integrity": "sha512-YXmXn3pA8abPOY//JtYxou95Ihvzmg8U6kQyolArkIyLd0pgVhrfor/iMsox8cn07WCOOvvuJ6XKegzIucPutQ==",
"requires": {
"backo2": "1.0.2",
"component-bind": "1.0.0",
"component-emitter": "~1.3.0",
"debug": "~3.1.0",
"engine.io-client": "~3.4.0",
"has-binary2": "~1.0.2",
"indexof": "0.0.1",
"parseqs": "0.0.6",
"parseuri": "0.0.6",
"socket.io-parser": "~3.3.0",
"to-array": "0.1.4"
},
"dependencies": {
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
"integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg=="
},
"debug": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
"requires": {
"ms": "2.0.0"
}
},
"isarray": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz",
"integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4="
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
},
"parseqs": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.6.tgz",
"integrity": "sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w=="
},
"parseuri": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/parseuri/-/parseuri-0.0.6.tgz",
"integrity": "sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow=="
},
"socket.io-parser": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.3.1.tgz",
"integrity": "sha512-1QLvVAe8dTz+mKmZ07Swxt+LAo4Y1ff50rlyoEx00TQmDFVQYPfcqGvIDJLGaBdhdNCecXtyKpD+EgKGcmmbuQ==",
"requires": {
"component-emitter": "~1.3.0",
"debug": "~3.1.0",
"isarray": "2.0.1"
}
}
}
},
"socket.io-parser": { "socket.io-parser": {
"version": "3.4.1", "version": "3.4.1",
"resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.4.1.tgz",
@@ -15929,20 +16160,20 @@
}, },
"dependencies": { "dependencies": {
"es-abstract": { "es-abstract": {
"version": "1.18.0-next.0", "version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==", "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": { "requires": {
"es-to-primitive": "^1.2.1", "es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
"has-symbols": "^1.0.1", "has-symbols": "^1.0.1",
"is-callable": "^1.2.0", "is-callable": "^1.2.2",
"is-negative-zero": "^2.0.0", "is-negative-zero": "^2.0.0",
"is-regex": "^1.1.1", "is-regex": "^1.1.1",
"object-inspect": "^1.8.0", "object-inspect": "^1.8.0",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.0", "object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1", "string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1" "string.prototype.trimstart": "^1.0.1"
} }
@@ -15979,6 +16210,17 @@
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
},
"object.assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
"integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.0",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
}
} }
} }
}, },
@@ -15992,19 +16234,19 @@
}, },
"dependencies": { "dependencies": {
"es-abstract": { "es-abstract": {
"version": "1.17.6", "version": "1.17.7",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
"requires": { "requires": {
"es-to-primitive": "^1.2.1", "es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
"has-symbols": "^1.0.1", "has-symbols": "^1.0.1",
"is-callable": "^1.2.0", "is-callable": "^1.2.2",
"is-regex": "^1.1.0", "is-regex": "^1.1.1",
"object-inspect": "^1.7.0", "object-inspect": "^1.8.0",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.0", "object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1", "string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1" "string.prototype.trimstart": "^1.0.1"
} }
@@ -16041,6 +16283,38 @@
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
},
"object.assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
"integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.0",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"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.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
}
}
} }
} }
}, },
@@ -16074,19 +16348,19 @@
}, },
"dependencies": { "dependencies": {
"es-abstract": { "es-abstract": {
"version": "1.17.6", "version": "1.17.7",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
"requires": { "requires": {
"es-to-primitive": "^1.2.1", "es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
"has-symbols": "^1.0.1", "has-symbols": "^1.0.1",
"is-callable": "^1.2.0", "is-callable": "^1.2.2",
"is-regex": "^1.1.0", "is-regex": "^1.1.1",
"object-inspect": "^1.7.0", "object-inspect": "^1.8.0",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.0", "object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1", "string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1" "string.prototype.trimstart": "^1.0.1"
} }
@@ -16123,6 +16397,38 @@
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
},
"object.assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
"integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.0",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"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.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
}
}
} }
} }
}, },
@@ -17073,19 +17379,19 @@
}, },
"dependencies": { "dependencies": {
"es-abstract": { "es-abstract": {
"version": "1.17.6", "version": "1.17.7",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz",
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==",
"requires": { "requires": {
"es-to-primitive": "^1.2.1", "es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1", "function-bind": "^1.1.1",
"has": "^1.0.3", "has": "^1.0.3",
"has-symbols": "^1.0.1", "has-symbols": "^1.0.1",
"is-callable": "^1.2.0", "is-callable": "^1.2.2",
"is-regex": "^1.1.0", "is-regex": "^1.1.1",
"object-inspect": "^1.7.0", "object-inspect": "^1.8.0",
"object-keys": "^1.1.1", "object-keys": "^1.1.1",
"object.assign": "^4.1.0", "object.assign": "^4.1.1",
"string.prototype.trimend": "^1.0.1", "string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1" "string.prototype.trimstart": "^1.0.1"
} }
@@ -17122,6 +17428,38 @@
"version": "1.8.0", "version": "1.8.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA=="
},
"object.assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
"integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.0",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz",
"integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==",
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.2",
"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.1",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
}
}
} }
} }
}, },
+3 -3
View File
@@ -17,9 +17,9 @@
"test": "npm run lint && npm run test-v3", "test": "npm run lint && npm run test-v3",
"lint": "standard --env mocha --fix", "lint": "standard --env mocha --fix",
"test-v3": "export NETWORK=mainnet && nyc --reporter=text mocha --timeout 60000 test/v3/", "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 && export TEST=integration && mocha --timeout 25000 test/v3/integration/price.js",
"test:integration": "mocha test/v3/integration", "test:integration": "mocha test/v3/integration",
"test:integration:slpdb": "mocha --timeout 25000 test/v3/integration/slp*.js", "test:integration:slpdb": "mocha --timeout 25000 -g '#validate2Single' test/v3/integration/slp*.js",
"coverage": "nyc report --reporter=text-lcov | coveralls", "coverage": "nyc report --reporter=text-lcov | coveralls",
"coverage:report": "export NETWORK=mainnet && nyc --reporter=html mocha --timeout 25000 test/v3/", "coverage:report": "export NETWORK=mainnet && nyc --reporter=html mocha --timeout 25000 test/v3/",
"docs": "./node_modules/.bin/apidoc -i src/routes/v3 -o docs" "docs": "./node_modules/.bin/apidoc -i src/routes/v3 -o docs"
@@ -28,7 +28,7 @@
"node": ">=10.15.1" "node": ">=10.15.1"
}, },
"dependencies": { "dependencies": {
"@psf/bch-js": "^3.6.3", "@psf/bch-js": "^3.7.1",
"apidoc": "^0.23.0", "apidoc": "^0.23.0",
"axios": "^0.19.0", "axios": "^0.19.0",
"bitcore-lib-cash": "^8.20.3", "bitcore-lib-cash": "^8.20.3",
+3
View File
@@ -39,6 +39,7 @@ const BlockbookV3 = require('./routes/v3/blockbook')
const Ninsight = require('./routes/v3/ninsight') const Ninsight = require('./routes/v3/ninsight')
const ElectrumXV3 = require('./routes/v3/electrumx') const ElectrumXV3 = require('./routes/v3/electrumx')
const EncryptionV3 = require('./routes/v3/encryption') const EncryptionV3 = require('./routes/v3/encryption')
const PriceV3 = require('./routes/v3/price')
require('dotenv').config() require('dotenv').config()
@@ -52,6 +53,7 @@ const blockbookV3 = new BlockbookV3()
const electrumxv3 = new ElectrumXV3() const electrumxv3 = new ElectrumXV3()
electrumxv3.connect() electrumxv3.connect()
const encryptionv3 = new EncryptionV3() const encryptionv3 = new EncryptionV3()
const pricev3 = new PriceV3()
const app = express() const app = express()
@@ -121,6 +123,7 @@ app.use(`/${v3prefix}/` + 'xpub', xpubV3.router)
app.use(`/${v3prefix}/` + 'blockbook', blockbookV3.router) app.use(`/${v3prefix}/` + 'blockbook', blockbookV3.router)
app.use(`/${v3prefix}/` + 'electrumx', electrumxv3.router) app.use(`/${v3prefix}/` + 'electrumx', electrumxv3.router)
app.use(`/${v3prefix}/` + 'encryption', encryptionv3.router) app.use(`/${v3prefix}/` + 'encryption', encryptionv3.router)
app.use(`/${v3prefix}/` + 'price', pricev3.router)
const ninsight = new Ninsight() const ninsight = new Ninsight()
app.use(`/${v3prefix}/` + 'ninsight', ninsight.router) app.use(`/${v3prefix}/` + 'ninsight', ninsight.router)
+55 -49
View File
@@ -22,9 +22,9 @@
'use strict' 'use strict'
const passport = require('passport') const passport = require('passport')
// const BasicStrategy = require('passport-http').BasicStrategy const BasicStrategy = require('passport-http').BasicStrategy
const AnonymousStrategy = require('passport-anonymous') const AnonymousStrategy = require('passport-anonymous')
// const wlogger = require('../util/winston-logging') const wlogger = require('../util/winston-logging')
// Used for debugging and iterrogating JS objects. // Used for debugging and iterrogating JS objects.
const util = require('util') const util = require('util')
@@ -33,9 +33,11 @@ util.inspect.defaultOptions = { depth: 1 }
// let _this // let _this
// Set default rate limit value for testing // 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
: 'testpassword'
// Convert the pro-tier password string into an array split by ':'. // 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}`) // wlogger.verbose(`PRO_PASS set to: ${PRO_PASS}`)
@@ -47,56 +49,60 @@ class AuthMW {
// Initialize passport for 'anonymous' authentication. // Initialize passport for 'anonymous' authentication.
passport.use(new AnonymousStrategy()) passport.use(new AnonymousStrategy())
console.log(`PRO_PASS: ${JSON.stringify(PRO_PASS, null, 2)}`)
// Initialize passport for 'basic' authentication. // Initialize passport for 'basic' authentication.
// passport.use( passport.use(
// new BasicStrategy({ passReqToCallback: true }, function ( new BasicStrategy({ passReqToCallback: true }, function (
// req, req,
// username, username,
// password, password,
// done done
// ) { ) {
// // console.log(`req: ${util.inspect(req)}`) // console.log(`req: ${util.inspect(req)}`)
// // console.log(`username: ${username}`) // console.log(`username: ${username}`)
// // console.log(`password: ${password}`) // console.log(`password: ${password}`)
//
// // Create the req.locals property if it does not yet exist. // Create the req.locals property if it does not yet exist.
// if (!req.locals) { if (!req.locals) {
// req.locals = { req.locals = {
// // default values // default values
// proLimit: false, proLimit: false,
// apiLevel: 0 apiLevel: 0
// } }
// } }
//
// // Set pro-tier rate limit to flag to false by default. // Set pro-tier rate limit to flag to false by default.
// req.locals.proLimit = false req.locals.proLimit = false
//
// // Evaluate the username and password and set the rate limit accordingly. // Evaluate the username and password and set the rate limit accordingly.
// // if (username === "BITBOX" && password === PRO_PASS) { // if (username === "BITBOX" && password === PRO_PASS) {
// if (username === 'BITBOX') { if (username === 'fullstackcash') {
// for (let i = 0; i < PRO_PASS.length; i++) { for (let i = 0; i < PRO_PASS.length; i++) {
// const thisPass = PRO_PASS[i] const thisPass = PRO_PASS[i]
//
// if (password === thisPass) { if (password === thisPass) {
// wlogger.verbose(`${req.url} called by ${password.slice(0, 6)}`) wlogger.verbose(`${req.url} called by ${password.slice(0, 6)}`)
//
// // Success // Success
// req.locals.proLimit = true req.locals.proLimit = true
// break console.log(`User ${req.ip} authenticated using Basic Auth`)
// } break
// } }
// } }
// }
// // console.log(`req.locals: ${util.inspect(req.locals)}`)
// // console.log(`req.locals: ${util.inspect(req.locals)}`)
// return done(null, true)
// }) return done(null, true)
// ) })
)
} }
// Middleware called by the route. // Middleware called by the route.
mw () { mw () {
return passport.authenticate(['anonymous'], { console.log('Initializing passport')
return passport.authenticate(['basic', 'anonymous'], {
session: false session: false
}) })
} }
+91 -69
View File
@@ -1,9 +1,23 @@
/*
Sets the rate limits for the anonymous and paid tiers. Current rate limits:
- 1000 points in 60 seconds
- 10 points per call for paid tier (100 RPM)
- 50 points per call for anonymous tier (20 RPM)
Background:
The rate limits below were originially coded with the idea of charging on a
per-resource basis. However, that was confusing to end users trying to purchase
a subscription. So everything was simplied to two tiers: paid and anonymous
*/
'use strict' 'use strict'
const jwt = require('jsonwebtoken')
const wlogger = require('../util/winston-logging') const wlogger = require('../util/winston-logging')
const config = require('../../config') const config = require('../../config')
const jwt = require('jsonwebtoken') const ANON_LIMITS = 50
// Redis // Redis
const redisOptions = { const redisOptions = {
@@ -98,74 +112,82 @@ class RateLimits {
wlogger.debug('No JWT token found!') wlogger.debug('No JWT token found!')
} }
// Used for displaying error message. Default value is 30. // Default value is 50 points per request = 20 RPM
let rateLimit = 30 let rateLimit = ANON_LIMITS
// Code here for the rate limiter is adapted from this example: // Only evaluate the JWT token if the user is not using Basic Authentication.
// https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#authorized-and-not-authorized-users if (!req.locals.proLimit) {
try { // Code here for the rate limiter is adapted from this example:
// The resource being consumed: full node, indexer, SLPDB, etc. // https://github.com/animir/node-rate-limiter-flexible/wiki/Overall-example#authorized-and-not-authorized-users
const resource = _this.getResource(req.url) try {
wlogger.debug(`resource: ${resource}`) // The resource being consumed: full node, indexer, SLPDB, etc.
const resource = _this.getResource(req.url)
wlogger.debug(`resource: ${resource}`)
let key = userId || req.ip let key = userId || req.ip
res.locals.key = key // Feedback for tests. res.locals.key = key // Feedback for tests.
// const pointsToConsume = userId ? 1 : 30 // const pointsToConsume = userId ? 1 : 30
decoded.resource = resource decoded.resource = resource
let pointsToConsume = _this.calcPoints(decoded) let pointsToConsume = _this.calcPoints(decoded)
res.locals.pointsToConsume = pointsToConsume // Feedback for tests.
// Retrieve the origin.
let origin = req.get('origin')
if (origin === undefined && key.indexOf('10.0.0.5') > -1) {
origin = 'slp-api'
}
wlogger.info(`origin: ${origin}`)
// If the request originates from one of the approved wallet apps, then
// apply paid-access rate limits.
if (
origin &&
(origin.toString().indexOf('wallet.fullstack.cash') > -1 ||
origin.toString().indexOf('sandbox.fullstack.cash') > -1 ||
origin === 'slp-api')
) {
pointsToConsume = 10
res.locals.pointsToConsume = pointsToConsume // Feedback for tests. res.locals.pointsToConsume = pointsToConsume // Feedback for tests.
// Retrieve the origin.
let origin = req.get('origin')
// Handle calls coming from the intranet.
if (origin === undefined && key.indexOf('10.0.0.5') > -1) {
origin = 'slp-api'
}
wlogger.info(`origin: ${origin}`)
// If the request originates from one of the approved wallet apps, then
// apply paid-access rate limits.
if (
origin &&
(origin.toString().indexOf('fullstack.cash') > -1 ||
origin.toString().indexOf('splitbch.com') > -1 ||
origin.toString().indexOf('slp-api') > -1)
) {
pointsToConsume = 10
res.locals.pointsToConsume = pointsToConsume // Feedback for tests.
}
// For internal calls, increase rate limits to as fast as possible.
if (
// Comment out the line below when running bch-js e2e rate limit tests.
key.toString().indexOf('::ffff:127.0.0.1') > -1 ||
// Do not comment out this line.
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}.`
)
rateLimit = Math.floor(1000 / pointsToConsume)
// Update the key so that rate limits track both the user and the resource.
key = `${key}-${resource}`
await _this.rateLimiter.consume(key, pointsToConsume)
} catch (err) {
// console.log('err: ', err)
// Used for returning data for tests.
res.locals.rateLimitTriggered = true
// console.log('res.locals: ', res.locals)
// Rate limited was triggered
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
error: `Too many requests. Your limits are currently ${rateLimit} requests per minute. Increase rate limits at https://fullstack.cash`
})
} }
// 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
) {
pointsToConsume = 1
res.locals.pointsToConsume = pointsToConsume // Feedback for tests.
}
wlogger.info(
`User ${key} consuming ${pointsToConsume} point for resource ${resource}.`
)
rateLimit = Math.floor(1000 / pointsToConsume)
// Update the key so that rate limits track both the user and the resource.
key = `${key}-${resource}`
await _this.rateLimiter.consume(key, pointsToConsume)
} catch (err) {
// console.log('err: ', err)
// Used for returning data for tests.
res.locals.rateLimitTriggered = true
// console.log('res.locals: ', res.locals)
// Rate limited was triggered
res.status(429) // https://github.com/Bitcoin-com/rest.bitcoin.com/issues/330
return res.json({
error: `Too many requests. Your limits are currently ${rateLimit} requests per minute. Increase rate limits at https://fullstack.cash`
})
} }
} catch (err) { } catch (err) {
wlogger.error('Error in route-ratelimit.js/newRateLimit(): ', err) wlogger.error('Error in route-ratelimit.js/newRateLimit(): ', err)
@@ -178,7 +200,7 @@ class RateLimits {
// Calculates the points consumed, based on the jwt information and the route // Calculates the points consumed, based on the jwt information and the route
// requested. // requested.
calcPoints (jwtInfo) { calcPoints (jwtInfo) {
let retVal = 300 // By default, use anonymous tier. let retVal = ANON_LIMITS // By default, use anonymous tier.
try { try {
// console.log(`jwtInfo: ${JSON.stringify(jwtInfo, null, 2)}`) // console.log(`jwtInfo: ${JSON.stringify(jwtInfo, null, 2)}`)
@@ -197,12 +219,12 @@ class RateLimits {
if (level40Routes.includes(resource)) { if (level40Routes.includes(resource)) {
if (apiLevel >= 40) retVal = 10 if (apiLevel >= 40) retVal = 10
// else if (apiLevel >= 10) retVal = 10 // else if (apiLevel >= 10) retVal = 10
else retVal = 100 else retVal = ANON_LIMITS
// Normal indexer routes // Normal indexer routes
} else if (level30Routes.includes(resource)) { } else if (level30Routes.includes(resource)) {
if (apiLevel >= 30) retVal = 10 if (apiLevel >= 30) retVal = 10
else retVal = 100 else retVal = ANON_LIMITS
// Full node tier // Full node tier
} else if (apiLevel >= 20) { } else if (apiLevel >= 20) {
@@ -210,7 +232,7 @@ class RateLimits {
// Free tier, full node only. // Free tier, full node only.
} else { } else {
retVal = 100 retVal = ANON_LIMITS
} }
} }
@@ -218,7 +240,7 @@ class RateLimits {
} catch (err) { } catch (err) {
wlogger.error('Error in route-ratelimit.js/calcPoints()') wlogger.error('Error in route-ratelimit.js/calcPoints()')
// throw err // throw err
retVal = 300 retVal = ANON_LIMITS
} }
return retVal return retVal
-70
View File
@@ -96,18 +96,6 @@ class Blockbook {
} }
} }
/**
* @api {get} /blockbook/balance/{addr} Get balance for a single address.
* @apiName Balance for a single address
* @apiGroup Blockbook
* @apiDescription Returns an object with balance and details about an address.
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v3/blockbook/balance/bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf" -H "accept: application/json"
*
*/
// GET handler for single balance
async balanceSingle (req, res, next) { async balanceSingle (req, res, next) {
try { try {
const address = req.params.address const address = req.params.address
@@ -165,18 +153,6 @@ class Blockbook {
} }
} }
/**
* @api {post} /blockbook/balance Get balance for an array of addresses.
* @apiName Balance for an array of addresses
* @apiGroup Blockbook
* @apiDescription Return balances and details for an array of addresses.
* Limited to 20 items per request.
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v3/blockbook/balance" -H "accept: application/json" -H "Content-Type: application/json" -d '{"addresses":["bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf","bitcoincash:qrdka2205f4hyukutc2g0s6lykperc8nsu5u2ddpqf"]}'
*
*
*/
// POST handler for bulk queries on address details // POST handler for bulk queries on address details
async balanceBulk (req, res, next) { async balanceBulk (req, res, next) {
try { try {
@@ -284,17 +260,6 @@ class Blockbook {
} }
} }
/**
* @api {get} /blockbook/utxos/{addr} Get utxos for a single address.
* @apiName UTXOs for a single address
* @apiGroup Blockbook
* @apiDescription Returns an object with UTXOs associated with an address.
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v3/blockbook/utxos/bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur3" -H "accept: application/json"
*
*/
// GET handler for single balance // GET handler for single balance
async utxosSingle (req, res, next) { async utxosSingle (req, res, next) {
try { try {
@@ -352,18 +317,6 @@ class Blockbook {
} }
} }
/**
* @api {post} /blockbook/utxos Get UTXOs for an array of addresses.
* @apiName UTXOs for an array of addresses
* @apiGroup Blockbook
* @apiDescription Return UTXOs associate with an array of addresses.
* Limited to 20 items per request.
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v3/blockbook/utxos" -H "accept: application/json" -H "Content-Type: application/json" -d '{"addresses":["bitcoincash:qr69kyzha07dcecrsvjwsj4s6slnlq4r8c30lxnur3","bitcoincash:qzy8wnj0dz927eu6kvh8v2pqsr5w8jh33ys757tdtq"]}'
*
*
*/
// POST handler for bulk queries on address utxos // POST handler for bulk queries on address utxos
async utxosBulk (req, res, next) { async utxosBulk (req, res, next) {
try { try {
@@ -461,17 +414,6 @@ class Blockbook {
} }
} }
/**
* @api {get} /blockbook/tx/{txid} Get details for a single transaction.
* @apiName Details for a single transaction
* @apiGroup Blockbook
* @apiDescription Returns an object with details for a single transaction
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v3/blockbook/tx/6181c669614fa18039a19b23eb06806bfece1f7514ab457c3bb82a40fe171a6d" -H "accept: application/json"
*
*/
// GET handler for single transaction details. // GET handler for single transaction details.
async txSingle (req, res, next) { async txSingle (req, res, next) {
try { try {
@@ -514,18 +456,6 @@ class Blockbook {
} }
} }
/**
* @api {post} /blockbook/tx Get details for an array of transactions.
* @apiName Details for an array of transactions
* @apiGroup Blockbook
* @apiDescription Return details for an array of transactions.
* Limited to 20 items per request.
*
* @apiExample Example usage:
* curl -X POST "https://api.fullstack.cash/v3/blockbook/tx" -H "accept: application/json" -H "Content-Type: application/json" -d '{"txids":["6181c669614fa18039a19b23eb06806bfece1f7514ab457c3bb82a40fe171a6d","6181c669614fa18039a19b23eb06806bfece1f7514ab457c3bb82a40fe171a6d"]}'
*
*
*/
// POST handler for bulk queries on tx details // POST handler for bulk queries on tx details
async txBulk (req, res, next) { async txBulk (req, res, next) {
try { try {
+18
View File
@@ -79,6 +79,21 @@ class Electrum {
// Set the connection flag. // Set the connection flag.
_this.isReady = true _this.isReady = true
// Periodically check the connection. If it's not connected, attempt to
// reconnect.
_this.reconnectIntervalHandle = setInterval(async function () {
const status = _this.electrumx.connection.status
// console.log(`Electrumx status: ${status}`)
// 1 = connected. If we're not connected, attemp to reconnect.
if (status !== 1) {
wlogger.info(`Electrumx not connectes. Status: ${status}`)
wlogger.info('Attempting to reconnect...')
await _this.electrumx.connect()
wlogger.info('...reconnected.')
}
}, 30000)
console.log('...Successfully connected to ElectrumX server.') console.log('...Successfully connected to ElectrumX server.')
// console.log(`_this.isReady: ${_this.isReady}`) // console.log(`_this.isReady: ${_this.isReady}`)
@@ -96,6 +111,9 @@ class Electrum {
// Return immediately if the isReady flag is false. // Return immediately if the isReady flag is false.
if (!_this.isReady) return true if (!_this.isReady) return true
// Disable the reconnect timer.
clearInterval(_this.reconnectIntervalHandle)
// Disconnect from the server. // Disconnect from the server.
await _this.electrumx.disconnect() await _this.electrumx.disconnect()
+15 -20
View File
@@ -16,15 +16,10 @@ const RouteUtils = require('../../util/route-utils')
const routeUtils = new RouteUtils() const routeUtils = new RouteUtils()
const BCHJS = require('@psf/bch-js') const BCHJS = require('@psf/bch-js')
const bchjs = new BCHJS() const restURL = process.env.LOCAL_RESTURL
? process.env.LOCAL_RESTURL
// Use Blockbook for getting indexer data. : 'https://api.fullstack.cash/v3/'
const Blockbook = require('./blockbook') const bchjs = new BCHJS({ restURL })
const blockbook = new Blockbook()
// Use RawTransactions library for getting raw blockchain data.
const RawTransactions = require('./full-node/rawtransactions')
const rawTransactions = new RawTransactions()
let _this let _this
@@ -36,8 +31,8 @@ class Encryption {
_this.axios = axios _this.axios = axios
_this.routeUtils = routeUtils _this.routeUtils = routeUtils
_this.bchjs = bchjs _this.bchjs = bchjs
_this.blockbook = blockbook // _this.blockbook = blockbook
_this.rawTransactions = rawTransactions // _this.rawTransactions = rawTransactions
_this.router = router _this.router = router
_this.router.get('/', _this.root) _this.router.get('/', _this.root)
@@ -115,23 +110,23 @@ class Encryption {
cashAddr cashAddr
) )
// Retrieve the transaction history for this address. const rawTxData = await _this.bchjs.Electrumx.transactions(cashAddr)
const balance = await _this.blockbook.balanceFromBlockbook(cashAddr) // console.log(`rawTxData: ${JSON.stringify(rawTxData, null, 2)}`)
// console.log(`balance: ${JSON.stringify(balance, null, 2)}`)
const txHistory = balance.txids // Extract just the TXIDs
// console.log(`txHistory: ${JSON.stringify(txHistory, null, 2)}`) const txids = rawTxData.transactions.map((elem) => elem.tx_hash)
// console.log(`txids: ${JSON.stringify(txids, null, 2)}`)
// throw error if there is no transaction history. // throw error if there is no transaction history.
if (!txHistory || txHistory.length === 0) { if (!txids || txids.length === 0) {
throw new Error('No transaction history.') throw new Error('No transaction history.')
} }
// Loop through the transaction history and search for the public key. // Loop through the transaction history and search for the public key.
for (let i = 0; i < txHistory.length; i++) { for (let i = 0; i < txids.length; i++) {
const thisTx = txHistory[i] const thisTx = txids[i]
const txDetails = await _this.rawTransactions.getRawTransactionsFromNode( const txDetails = await _this.bchjs.RawTransactions.getRawTransaction(
thisTx, thisTx,
true true
) )
+155
View File
@@ -0,0 +1,155 @@
/*
This price route is really just a wrapper for another API price endpoint.
The main reason for hosting this price wrapper is so that the price can
be accessible over Tor. Tor is typically blocked by other REST API servers.
*/
'use strict'
const express = require('express')
const axios = require('axios')
const wlogger = require('../../util/winston-logging')
const util = require('util')
const RouteUtils = require('../../util/route-utils')
const routeUtils = new RouteUtils()
let _this // Global context for 'this' instance of the Class.
class Price {
constructor () {
_this = this
this.axios = axios
this.routeUtils = routeUtils
this.priceUrl = 'https://api.coinbase.com/v2/exchange-rates?currency=BCH'
this.coinexPriceUrl =
'https://api.coinex.com/v1/market/ticker?market=bchausdt'
this.router = express.Router()
this.router.get('/', _this.root)
this.router.get('/usd', _this.getUSD)
this.router.get('/rates', _this.getBCHRate)
this.router.get('/bchausd', _this.getBCHAUSD)
}
// DRY error handler.
errorHandler (err, res) {
// Attempt to decode the error message.
const { msg, status } = _this.routeUtils.decodeError(err)
if (msg) {
res.status(status)
return res.json({ error: msg })
}
res.status(500)
return res.json({ error: util.inspect(err) })
}
// Root API endpoint. Simply acknowledges that it exists.
root (req, res, next) {
return res.json({ status: 'price' })
}
/**
* @api {get} /price/usd Get the USD price of BCH
* @apiName Get the USD price of BCH
* @apiGroup Price
* @apiDescription Get the USD price of BCH from Coinbase.
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v3/price/usd" -H "accept: application/json"
*
*/
async getUSD (req, res, next) {
try {
// Request options
const opt = {
method: 'get',
baseURL: _this.priceUrl,
timeout: 15000
}
const response = await axios.request(opt)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
return res.json({ usd: Number(response.data.data.rates.USD) })
} catch (err) {
// Write out error to error log.
wlogger.error('Error in price.js/getUSD().', err)
return _this.errorHandler(err, res)
}
}
/**
* @api {get} /price/usd Get rates for several different currencies
* @apiName Get rates for several different currencies
* @apiGroup Price
* @apiDescription Get rates for several different currencies from Coinbase.
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v3/price/rates" -H "accept: application/json"
*
*/
// Get rates for several different currencies
async getBCHRate (req, res, next) {
try {
// Request options
const opt = {
method: 'get',
baseURL: _this.priceUrl,
timeout: 15000
}
const response = await axios.request(opt)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
return res.json(response.data.data.rates)
} catch (err) {
// Write out error to error log.
wlogger.error('Error in price.js/getUSD().', err)
return _this.errorHandler(err, res)
}
}
/**
* @api {get} /price/bchausd Get the USD price of BCHA
* @apiName Get the USD price of BCHA
* @apiGroup Price
* @apiDescription Get the USD price of BCHA from Coinex.
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v3/price/bchausd" -H "accept: application/json"
*
*/
async getBCHAUSD (req, res, next) {
try {
// Request options
const opt = {
method: 'get',
baseURL: _this.coinexPriceUrl,
timeout: 15000
}
const response = await axios.request(opt)
// console.log(`response.data: ${JSON.stringify(response.data, null, 2)}`)
const price = Number(response.data.data.ticker.last)
return res.json({ usd: price })
} catch (err) {
// Write out error to error log.
wlogger.error('Error in price.js/getBCHAUSD().', err)
return _this.errorHandler(err, res)
}
}
}
module.exports = Price
+94 -9
View File
@@ -13,6 +13,7 @@ const Slpdb = require('./services/slpdb')
// const strftime = require('strftime') // const strftime = require('strftime')
const wlogger = require('../../util/winston-logging') const wlogger = require('../../util/winston-logging')
// Instantiate a local copy of bch-js using the local REST API server.
const LOCAL_RESTURL = process.env.LOCAL_RESTURL const LOCAL_RESTURL = process.env.LOCAL_RESTURL
? process.env.LOCAL_RESTURL ? process.env.LOCAL_RESTURL
: 'https://api.fullstack.cash/v3/' : 'https://api.fullstack.cash/v3/'
@@ -51,6 +52,7 @@ class Slp {
constructor () { constructor () {
_this = this _this = this
// Encapsulate external libraries.
_this.axios = axios _this.axios = axios
_this.routeUtils = routeUtils _this.routeUtils = routeUtils
_this.BigNumber = BigNumber _this.BigNumber = BigNumber
@@ -71,6 +73,7 @@ class Slp {
_this.router.post('/convert', _this.convertAddressBulk) _this.router.post('/convert', _this.convertAddressBulk)
_this.router.post('/validateTxid', _this.validateBulk) _this.router.post('/validateTxid', _this.validateBulk)
_this.router.get('/validateTxid/:txid', _this.validateSingle) _this.router.get('/validateTxid/:txid', _this.validateSingle)
_this.router.get('/validateTxid2/:txid', _this.validate2Single)
_this.router.get('/txDetails/:txid', _this.txDetails) _this.router.get('/txDetails/:txid', _this.txDetails)
_this.router.get('/tokenStats/:tokenId', _this.tokenStats) _this.router.get('/tokenStats/:tokenId', _this.tokenStats)
_this.router.get( _this.router.get(
@@ -1062,7 +1065,7 @@ class Slp {
* @api {get} /slp/validateTxid/{txid} Validate single SLP transaction by txid. * @api {get} /slp/validateTxid/{txid} Validate single SLP transaction by txid.
* @apiName Validate single SLP transaction by txid. * @apiName Validate single SLP transaction by txid.
* @apiGroup SLP * @apiGroup SLP
* @apiDescription Validate single SLP transaction by txid. * @apiDescription Validate single SLP transaction by txid, using SLPDB.
* *
* *
* @apiExample Example usage: * @apiExample Example usage:
@@ -1135,11 +1138,66 @@ class Slp {
} }
} }
// Returns a Boolean if the input TXID is a valid SLP TXID. /**
// async function isValidSlpTxid (txid) { * @api {get} /slp/validateTxid2/{txid} Validate single SLP transaction by txid.
// const isValid = await slpValidator.isValidSlpTxid(txid) * @apiName Validate single SLP transaction by txid.
// return isValid * @apiGroup SLP
// } * @apiDescription Validate single SLP transaction by txid, using slp-validate.
* Slower, less efficient method of validating an SLP TXID using the slp-validate
* npm library. This method is independent of SLPDB and can be used as a fall-back
* when SLPDB returns 'null' values.
*
*
* @apiExample Example usage:
* curl -X GET "https://api.fullstack.cash/v3/slp/validateTxid2/f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a" -H "accept:application/json"
*
*
*/
async validate2Single (req, res, next) {
try {
const txid = req.params.txid
// Validate input
if (!txid || txid === '') {
res.status(400)
return res.json({ error: 'txid can not be empty' })
}
wlogger.debug(
'Executing slp/validate2Single/:txid with this txid: ',
txid
)
// null by default.
// Default return value.
const result = {
txid: txid,
isValid: null,
msg: ''
}
// Request options
const opt = {
method: 'get',
baseURL: `${process.env.SLP_API_URL}slp/validate/${txid}`,
timeout: 10000 // Exit after 10 seconds.
}
const tokenRes = await _this.axios.request(opt)
// console.log(`tokenRes.data: ${JSON.stringify(tokenRes.data, null, 2)}`)
// console.log(`tokenRes: `, tokenRes)
// Overwrite the default value with the result from slp-api.
result.isValid = tokenRes.data.isValid
res.status(200)
return res.json(result)
} catch (err) {
// console.log('validate2Single error: ', err)
wlogger.error('Error in slp.ts/validate2Single().', err)
return _this.errorHandler(err, res)
}
}
/** /**
* @api {get} /slp/txDetails/{txid} SLP transaction details. * @api {get} /slp/txDetails/{txid} SLP transaction details.
@@ -1193,7 +1251,9 @@ class Slp {
const tokenRes = await _this.axios.request(opt) const tokenRes = await _this.axios.request(opt)
// console.log(`tokenRes: ${util.inspect(tokenRes)}`) // console.log(`tokenRes: ${util.inspect(tokenRes)}`)
if (tokenRes.data.c.length === 0) { // Return 'not found' error if both the confirmed and unconfirmed
// collections are empty.
if (tokenRes.data.c.length === 0 && tokenRes.data.u.length === 0) {
res.status(404) res.status(404)
return res.json({ error: 'TXID not found' }) return res.json({ error: 'TXID not found' })
} }
@@ -1379,12 +1439,29 @@ class Slp {
sendOutputs.push(string.toString()) sendOutputs.push(string.toString())
}) })
// Because you are not using Insight API indexer, you do not get the
// sending addresses from an indexer or from the node.
// However, they are available from the SLPDB output.
const tokenInputs = transaction.in
// Collect the input addresses
const sendInputs = []
for (let i = 0; i < tokenInputs.length; i += 1) {
const tokenInput = tokenInputs[i]
const sendInput = {}
sendInput.address = tokenInput.e.a
sendInputs.push(sendInput)
}
const obj = { const obj = {
tokenInfo: { tokenInfo: {
versionType: transaction.slp.detail.versionType, versionType: transaction.slp.detail.versionType,
tokenName: transaction.slp.detail.name,
tokenTicker: transaction.slp.detail.symbol,
transactionType: transaction.slp.detail.transactionType, transactionType: transaction.slp.detail.transactionType,
tokenIdHex: transaction.slp.detail.tokenIdHex, tokenIdHex: transaction.slp.detail.tokenIdHex,
sendOutputs: sendOutputs sendOutputs: sendOutputs,
sendInputsFull: sendInputs,
sendOutputsFull: transaction.slp.detail.outputs
}, },
tokenIsValid: transaction.slp.valid tokenIsValid: transaction.slp.valid
} }
@@ -1540,7 +1617,7 @@ class Slp {
* not been confirmed. * not been confirmed.
* *
* @apiExample Example usage: * @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}]}' * curl -X POST "https://api.fullstack.cash/v3/slp/hydrateUtxos" -H "accept:application/json" -H "Content-Type: application/json" -d '{"utxos":[{"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}]}]}'
* *
* *
*/ */
@@ -1570,6 +1647,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. // Loop through each address and query the UTXOs for that element.
for (let i = 0; i < utxos.length; i++) { for (let i = 0; i < utxos.length; i++) {
const theseUtxos = utxos[i].utxos const theseUtxos = utxos[i].utxos
@@ -1586,6 +1670,7 @@ class Slp {
return res.json({ slpUtxos: utxos }) return res.json({ slpUtxos: utxos })
} catch (err) { } catch (err) {
wlogger.error('Error in slp.js/hydrateUtxos().', err) wlogger.error('Error in slp.js/hydrateUtxos().', err)
console.error('Error in slp.js/hydrateUtxos().', err)
// Decode the error message. // Decode the error message.
const { msg, status } = routeUtils.decodeError(err) const { msg, status } = routeUtils.decodeError(err)
+9
View File
@@ -20,4 +20,13 @@ export FULCRUM_PORT=50002
export TOKENSECRET=somelongpassword export TOKENSECRET=somelongpassword
# So that bch-api can call bch-js locally.
export LOCAL_RESTURL=http://127.0.0.1:3000/v3/
# slp-api alternative SLP validator.
export SLP_API_URL=http://10.0.0.5:5001/
# Basic Authentication password
export PRO_PASS=somerandomepassword:someotherrandompassword:aThirdPassword
npm start npm start
+1 -1
View File
@@ -42,7 +42,7 @@ function expectRouteError (res, result, expectedError, code = 400) {
assert.equal(result.success, false) assert.equal(result.success, false)
} }
describe('#ElectrumX Router', () => { describe('#Electrumx', () => {
let req, res let req, res
let sandbox let sandbox
const electrumxRoute = new ElecrumxRoute() const electrumxRoute = new ElecrumxRoute()
+5 -1
View File
@@ -821,6 +821,7 @@ describe('#BlockchainRouter', () => {
]) ])
}) })
}) })
describe('getRawMempool()', () => { describe('getRawMempool()', () => {
it('should throw 503 when network issues', async () => { it('should throw 503 when network issues', async () => {
// Save the existing RPC URL. // Save the existing RPC URL.
@@ -842,6 +843,7 @@ describe('#BlockchainRouter', () => {
'Error message expected' 'Error message expected'
) )
}) })
it('returns proper error when downstream service stalls', async () => { it('returns proper error when downstream service stalls', async () => {
// Mock the timeout error. // Mock the timeout error.
sandbox.stub(uut.axios, 'request').throws({ code: 'ECONNABORTED' }) sandbox.stub(uut.axios, 'request').throws({ code: 'ECONNABORTED' })
@@ -856,6 +858,7 @@ describe('#BlockchainRouter', () => {
'Error message expected' 'Error message expected'
) )
}) })
it('returns proper error when downstream service is down', async () => { it('returns proper error when downstream service is down', async () => {
// Mock the timeout error. // Mock the timeout error.
sandbox.stub(uut.axios, 'request').throws({ code: 'ECONNREFUSED' }) sandbox.stub(uut.axios, 'request').throws({ code: 'ECONNREFUSED' })
@@ -871,7 +874,7 @@ describe('#BlockchainRouter', () => {
) )
}) })
it('should GET /getMempoolInfo', async () => { it('should GET /getRawMempool', async () => {
// Mock the RPC call for unit tests. // Mock the RPC call for unit tests.
if (process.env.TEST === 'unit') { if (process.env.TEST === 'unit') {
sandbox sandbox
@@ -886,6 +889,7 @@ describe('#BlockchainRouter', () => {
// Not sure what other assertions should be made here. // Not sure what other assertions should be made here.
}) })
}) })
describe('getMempoolEntrySingle()', () => { describe('getMempoolEntrySingle()', () => {
it('should throw 400 if txid is empty', async () => { it('should throw 400 if txid is empty', async () => {
const result = await uut.getMempoolEntrySingle(req, res) const result = await uut.getMempoolEntrySingle(req, res)
+9 -9
View File
@@ -78,11 +78,11 @@ describe('#Encryption Router', () => {
// Mock the Insight URL for unit tests. // Mock the Insight URL for unit tests.
if (process.env.TEST === 'unit') { if (process.env.TEST === 'unit') {
sandbox sandbox
.stub(encryptionRoute.blockbook, 'balanceFromBlockbook') .stub(encryptionRoute.bchjs.Electrumx, 'transactions')
.resolves(mockData.mockBalance) .resolves(mockData.mockFulcrumTxHistory)
sandbox sandbox
.stub(encryptionRoute.rawTransactions, 'getRawTransactionsFromNode') .stub(encryptionRoute.bchjs.RawTransactions, 'getRawTransaction')
.resolves(mockData.mockTxDetails) .resolves(mockData.mockTxDetails2)
} }
const result = await encryptionRoute.getPublicKey(req, res) const result = await encryptionRoute.getPublicKey(req, res)
@@ -105,8 +105,8 @@ describe('#Encryption Router', () => {
// Mock the Insight URL for unit tests. // Mock the Insight URL for unit tests.
if (process.env.TEST === 'unit') { if (process.env.TEST === 'unit') {
sandbox sandbox
.stub(encryptionRoute.blockbook, 'balanceFromBlockbook') .stub(encryptionRoute.bchjs.Electrumx, 'transactions')
.resolves(mockData.mockNoTxHistory) .resolves(mockData.mockFulcrumNoTxHistory)
} }
const result = await encryptionRoute.getPublicKey(req, res) const result = await encryptionRoute.getPublicKey(req, res)
@@ -126,10 +126,10 @@ describe('#Encryption Router', () => {
// Mock the Insight URL for unit tests. // Mock the Insight URL for unit tests.
if (process.env.TEST === 'unit') { if (process.env.TEST === 'unit') {
sandbox sandbox
.stub(encryptionRoute.blockbook, 'balanceFromBlockbook') .stub(encryptionRoute.bchjs.Electrumx, 'transactions')
.resolves(mockData.mockNoSendBalance) .resolves(mockData.mockFulcrumNoSendBalance)
sandbox sandbox
.stub(encryptionRoute.rawTransactions, 'getRawTransactionsFromNode') .stub(encryptionRoute.bchjs.RawTransactions, 'getRawTransaction')
.resolves(mockData.mockNoSendTx) .resolves(mockData.mockNoSendTx)
} }
+45
View File
@@ -0,0 +1,45 @@
/*
Integration tests for the price library.
*/
'use strict'
const assert = require('chai').assert
// const axios = require('axios')
// Used for debugging.
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
const Price = require('../../../src/routes/v3/price')
const price = new Price()
const { mockReq, mockRes } = require('../mocks/express-mocks')
describe('#price', () => {
let req, res
beforeEach(() => {
// Mock the req and res objects used by Express routes.
req = mockReq
res = mockRes
})
describe('#getUSD', () => {
it('should get the USD price', async () => {
const result = await price.getUSD(req, res)
console.log(`result: ${util.inspect(result)}`)
assert.isNumber(result.usd)
})
})
describe('#getBCHAUSD', () => {
it('should get the USD price of BCHA', async () => {
const result = await price.getBCHAUSD(req, res)
console.log(`result: ${util.inspect(result)}`)
assert.isNumber(result.usd)
})
})
})
+38 -40
View File
@@ -1,7 +1,6 @@
/* /*
This integration tests should be run against a live bch-api REST server. It These tests have been deprecated. To test bch-api rate limits, run the e2e
tests to ensure the rate-limits are working as expected. Adjust the values tests in the bch-js repository.
in the tests below to match the rate limit setting in your own installation.
*/ */
'use strict' 'use strict'
@@ -17,11 +16,11 @@ util.inspect.defaultOptions = { depth: 1 }
// const SERVER = `http://192.168.0.36:12400/v3/` // const SERVER = `http://192.168.0.36:12400/v3/`
const SERVER = 'http://localhost:3000/v3/' const SERVER = 'http://localhost:3000/v3/'
// const SERVER = 'https://api.fullstack.cash/v3/' // const SERVER = 'https://api.fullstack.cash/v3/'
//
// const TEST_JWT =
// 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2JmMDIyMWMxMDAxMmFkOTNmZiIsImVtYWlsIjoiY2hyaXMudHJvdXRuZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjQwLCJyYXRlTGltaXQiOjMsImlhdCI6MTYwMDYyODk1MSwiZXhwIjoxNjAzMjIwOTUxfQ.JPXDJQsxJFtCGZjHOd-hRfJuY41Ef_FQ4ET06CtYdNk'
const TEST_JWT = describe('#JWT rate limits', () => {
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlODhhY2JmMDIyMWMxMDAxMmFkOTNmZiIsImVtYWlsIjoiY2hyaXMudHJvdXRuZXJAZ21haWwuY29tIiwiYXBpTGV2ZWwiOjQwLCJyYXRlTGltaXQiOjMsImlhdCI6MTYwMDYyODk1MSwiZXhwIjoxNjAzMjIwOTUxfQ.JPXDJQsxJFtCGZjHOd-hRfJuY41Ef_FQ4ET06CtYdNk'
describe('#rate limits', () => {
it('should get control/getNetworkInfo() with no auth', async () => { it('should get control/getNetworkInfo() with no auth', async () => {
const options = { const options = {
method: 'GET', method: 'GET',
@@ -36,25 +35,24 @@ describe('#rate limits', () => {
assert.hasAnyKeys(result.data, ['version']) assert.hasAnyKeys(result.data, ['version'])
}) })
it('should trigger rate-limit handler if rate limits exceeds 5 request per minute', async () => { it('should trigger rate-limit handler if rate limits exceeds 20 request per minute', async () => {
try { try {
// Actual rate limit is 60 per minute X 4 nodes = 240 rpm.
const options = { const options = {
method: 'GET', method: 'GET',
url: `${SERVER}control/getNetworkInfo` url: `${SERVER}control/getNetworkInfo`
} }
const promises = [] const promises = []
for (let i = 0; i < 5; i++) { for (let i = 0; i < 30; i++) {
const promise = axios(options) const promise = axios(options)
promises.push(promise) promises.push(promise)
} }
await Promise.all(promises) await Promise.all(promises)
assert.equal(true, false, 'Unexpected result!') assert.fail('Unexpected result!')
} catch (err) { } catch (err) {
// console.log(`err.response: ${util.inspect(err.response)}`) console.log('err: ', err)
assert.equal(err.response.status, 429) assert.equal(err.response.status, 429)
assert.include(err.response.data.error, 'Too many requests') assert.include(err.response.data.error, 'Too many requests')
@@ -135,32 +133,32 @@ describe('#rate limits', () => {
// } // }
// }) // })
it('should unlock pro-tier for a valid JWT token', async () => { // it('should unlock pro-tier for a valid JWT token', async () => {
try { // try {
// Actual rate limit is 60 per minute X 4 nodes = 240 rpm. // // Actual rate limit is 60 per minute X 4 nodes = 240 rpm.
const options = { // const options = {
method: 'GET', // method: 'GET',
url: `${SERVER}control/`, // url: `${SERVER}control/`,
headers: { // headers: {
Authorization: `Token ${TEST_JWT}` // Authorization: `Token ${TEST_JWT}`
} // }
} // }
//
const promises = [] // const promises = []
for (let i = 0; i < 60; i++) { // for (let i = 0; i < 60; i++) {
const promise = axios(options) // const promise = axios(options)
promises.push(promise) // promises.push(promise)
} // }
//
await Promise.all(promises) // await Promise.all(promises)
//
// assert.equal(true, false, "Unexpected result!") // // assert.equal(true, false, "Unexpected result!")
assert.equal(true, true, 'Not throwing an error is a pass!') // assert.equal(true, true, 'Not throwing an error is a pass!')
} catch (err) { // } catch (err) {
console.log(`err.response: ${util.inspect(err.response)}`) // console.log(`err.response: ${util.inspect(err.response)}`)
//
assert.equal(true, false, 'Unexpected result!') // assert.equal(true, false, 'Unexpected result!')
} // }
// Override default timeout for this test. // // Override default timeout for this test.
}).timeout(20000) // }).timeout(20000)
}) })
+45
View File
@@ -213,4 +213,49 @@ describe('#slp', () => {
assert.equal(result.slpUtxos[2].utxos.length, 2) assert.equal(result.slpUtxos[2].utxos.length, 2)
}) })
}) })
describe('#validate2Single', () => {
it('should invalidate a known invalid TXID', async () => {
const txid =
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
req.params.txid = txid
const result = await slp.validate2Single(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result.txid, txid)
assert.equal(result.isValid, false)
})
it('should validate a known valid TXID', async () => {
const txid =
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488'
req.params.txid = txid
const result = await slp.validate2Single(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result.txid, txid)
assert.equal(result.isValid, true)
})
// CT 10-11-2020: This test is valid, but because of the cacheing built
// into slp-validate, it will not consistently pass or fail. To manually
// Run this test, re-start slp-api or find a token txid with a long DAG.
// it('should cancel if validation takes too long', async () => {
// const txid =
// 'eacb1085dfa296fef6d4ae2c0f4529a1bef096dd2325bdcc6dcb5241b3bdb579'
//
// req.params.txid = txid
// const result = await slp.validate2Single(req, res)
// // console.log(`result: ${JSON.stringify(result, null, 2)}`)
//
// assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
// assert.include(
// result.error,
// 'Could not communicate with full node',
// 'Error message expected'
// )
// })
})
}) })
+139 -136
View File
@@ -2,126 +2,6 @@
Mock data for the encryption test library Mock data for the encryption test library
*/ */
const mockBalance = {
page: 1,
totalPages: 1,
itemsOnPage: 1000,
address: 'bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0',
balance: '582434',
totalReceived: '213083250',
totalSent: '212500816',
unconfirmedBalance: '0',
unconfirmedTxs: 0,
txs: 12,
txids: [
'c42f8f16d3baa2ee343ea89ef110dfe094992379d08edd30887b8ca7ee671c9a',
'1afcc63b244182647909539ebe3f4a44b8ea4120a95edb8d9eebe5347b9491bb',
'e4a0ac48ff3f42fc342717a2a3d34248e5e85bae79d59bd20e1b60e61b1c500f',
'0f9b49cafeb9ae1d741cdb12137c92816aa8470944c270a78ba2e610bd59190d',
'ceb0cab0e37b59caf3ca29e1a698d19ff47f2827dd09cb2f3b91b9100b1dad1c',
'8bc2134c7e48e56e1769b3d7c4c1e3a0acc68e1e58160eee6fa67f3208c07262',
'b3792d28377b975560e1b6f09e48aeff8438d4c6969ca578bd406393bd50bd7d',
'ecc1b51bac767880382bf3190ff17abf78d0936843a022a943d871116ed50368',
'9ea667bcfc9cd337bd6c5583d8094c1b1942bd2015d95b54189deac5070eeff0',
'6960255abe64893073921e96bf3c053c82686e0fc22a565494fbe2a31e766975',
'7e9aa7a74de2b30200a2d6fc748ff35a0c753221444194f720bb7f61ef1d9153',
'eff00a9538487ff44243c75fb13de19b5783454c42c81b9aff9afbfd09cbaec3'
]
}
const mockTxDetails = {
txid: '1afcc63b244182647909539ebe3f4a44b8ea4120a95edb8d9eebe5347b9491bb',
hash: '1afcc63b244182647909539ebe3f4a44b8ea4120a95edb8d9eebe5347b9491bb',
version: 1,
size: 437,
locktime: 0,
vin: [
{
txid: 'c42f8f16d3baa2ee343ea89ef110dfe094992379d08edd30887b8ca7ee671c9a',
vout: 0,
scriptSig: {
asm:
'30450221008052d3b067418d53585fb8f91e1b57cf3c040dc9c07a70f393ed663b3f7502c50220749aa8e09ac922e78cb474c8097873cfb2634108d7acaa7db32a73a35743da97[ALL|FORKID] 044eb40b025df18409f2a5197b010dd62a9e65d9a74e415e5b10367721a9c4baa7ebfee22d14b8ece1c9bd70c0d9e5e8b00b61b81b88a1b5ce6f24eac6b8a34b2c',
hex:
'4830450221008052d3b067418d53585fb8f91e1b57cf3c040dc9c07a70f393ed663b3f7502c50220749aa8e09ac922e78cb474c8097873cfb2634108d7acaa7db32a73a35743da974141044eb40b025df18409f2a5197b010dd62a9e65d9a74e415e5b10367721a9c4baa7ebfee22d14b8ece1c9bd70c0d9e5e8b00b61b81b88a1b5ce6f24eac6b8a34b2c'
},
sequence: 4294967295
},
{
txid: 'e4a0ac48ff3f42fc342717a2a3d34248e5e85bae79d59bd20e1b60e61b1c500f',
vout: 1,
scriptSig: {
asm:
'3044022050d7fe7cdcec81eefa0987b88ddb83274d8e9063d927090dc4c2d1db76c512d302207dc1eea439a627476265ed87f59cc9823fb572ffc2640f0218d7bddc9a621c6e[ALL|FORKID] 044eb40b025df18409f2a5197b010dd62a9e65d9a74e415e5b10367721a9c4baa7ebfee22d14b8ece1c9bd70c0d9e5e8b00b61b81b88a1b5ce6f24eac6b8a34b2c',
hex:
'473044022050d7fe7cdcec81eefa0987b88ddb83274d8e9063d927090dc4c2d1db76c512d302207dc1eea439a627476265ed87f59cc9823fb572ffc2640f0218d7bddc9a621c6e4141044eb40b025df18409f2a5197b010dd62a9e65d9a74e415e5b10367721a9c4baa7ebfee22d14b8ece1c9bd70c0d9e5e8b00b61b81b88a1b5ce6f24eac6b8a34b2c'
},
sequence: 4294967295
}
],
vout: [
{
value: 0.47,
n: 0,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 7ab928d0b41194411a2e87a782b688c7cc69ba46 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a9147ab928d0b41194411a2e87a782b688c7cc69ba4688ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qpatj2xsksgegsg696r60q4k3rruc6d6gc3srp333v']
}
},
{
value: 0.00531373,
n: 1,
scriptPubKey: {
asm:
'OP_DUP OP_HASH160 f3707320bbb4a28759a78a5ad63a77a2f5d462ec OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a914f3707320bbb4a28759a78a5ad63a77a2f5d462ec88ac',
reqSigs: 1,
type: 'pubkeyhash',
addresses: ['bitcoincash:qrehqueqhw629p6e57994436w730t4rzasnly00ht0']
}
}
],
hex:
'01000000029a1c67eea78c7b8830dd8ed079239994e0df10f19ea83e34eea2bad3168f2fc4000000008b4830450221008052d3b067418d53585fb8f91e1b57cf3c040dc9c07a70f393ed663b3f7502c50220749aa8e09ac922e78cb474c8097873cfb2634108d7acaa7db32a73a35743da974141044eb40b025df18409f2a5197b010dd62a9e65d9a74e415e5b10367721a9c4baa7ebfee22d14b8ece1c9bd70c0d9e5e8b00b61b81b88a1b5ce6f24eac6b8a34b2cffffffff0f501c1be6601b0ed29bd579ae5be8e54842d3a3a2172734fc423fff48aca0e4010000008a473044022050d7fe7cdcec81eefa0987b88ddb83274d8e9063d927090dc4c2d1db76c512d302207dc1eea439a627476265ed87f59cc9823fb572ffc2640f0218d7bddc9a621c6e4141044eb40b025df18409f2a5197b010dd62a9e65d9a74e415e5b10367721a9c4baa7ebfee22d14b8ece1c9bd70c0d9e5e8b00b61b81b88a1b5ce6f24eac6b8a34b2cffffffff02c029cd02000000001976a9147ab928d0b41194411a2e87a782b688c7cc69ba4688acad1b0800000000001976a914f3707320bbb4a28759a78a5ad63a77a2f5d462ec88ac00000000',
blockhash: '0000000000000000045e5e52fb4f9746b3d15d3062855fd346aaef3debef4360',
confirmations: 71465,
time: 1545564654,
blocktime: 1545564654
}
const mockNoTxHistory = {
page: 1,
totalPages: 1,
itemsOnPage: 1000,
address: 'bitcoincash:qqwfpk04ecf69wuprj9yjys9rla5mk7rj5j8uthqel',
balance: '0',
totalReceived: '0',
totalSent: '0',
unconfirmedBalance: '0',
unconfirmedTxs: 0,
txs: 0
}
const mockNoSendBalance = {
page: 1,
totalPages: 1,
itemsOnPage: 1000,
address: 'bitcoincash:qq78nwj5x97yh6wtlfd27dtlwjuh70vkjc59h8tgtg',
balance: '0',
totalReceived: '0',
totalSent: '0',
unconfirmedBalance: '600',
unconfirmedTxs: 1,
txs: 0,
txids: [
'a3b62cd4f4c56ba52139179db14bffd4ab22a2e077f3c62bd5cf0541bfcaf023'
]
}
const mockNoSendTx = { const mockNoSendTx = {
txid: 'a3b62cd4f4c56ba52139179db14bffd4ab22a2e077f3c62bd5cf0541bfcaf023', txid: 'a3b62cd4f4c56ba52139179db14bffd4ab22a2e077f3c62bd5cf0541bfcaf023',
hash: 'a3b62cd4f4c56ba52139179db14bffd4ab22a2e077f3c62bd5cf0541bfcaf023', hash: 'a3b62cd4f4c56ba52139179db14bffd4ab22a2e077f3c62bd5cf0541bfcaf023',
@@ -133,8 +13,10 @@ const mockNoSendTx = {
txid: '681cc1d392975a7e78cbd490b8a940d7eb029c554f2cee332e3a7119d77fb189', txid: '681cc1d392975a7e78cbd490b8a940d7eb029c554f2cee332e3a7119d77fb189',
vout: 1, vout: 1,
scriptSig: { scriptSig: {
asm: '30450221008a7c86ce3a9f5765573440143a00631bb5afa9a58b24674a7fa794bb11ffffc30220397dd4c672c3e0dbe0c4eb6390b5727c5fdc3a42cf3ae275ac13b44d27bb234f[ALL|FORKID] 02bc9fc26ae2bd9a9a14e9baeda4b4fdd95a00bcbf375732c647488b3ce82a2fd7', asm:
hex: '4830450221008a7c86ce3a9f5765573440143a00631bb5afa9a58b24674a7fa794bb11ffffc30220397dd4c672c3e0dbe0c4eb6390b5727c5fdc3a42cf3ae275ac13b44d27bb234f412102bc9fc26ae2bd9a9a14e9baeda4b4fdd95a00bcbf375732c647488b3ce82a2fd7' '30450221008a7c86ce3a9f5765573440143a00631bb5afa9a58b24674a7fa794bb11ffffc30220397dd4c672c3e0dbe0c4eb6390b5727c5fdc3a42cf3ae275ac13b44d27bb234f[ALL|FORKID] 02bc9fc26ae2bd9a9a14e9baeda4b4fdd95a00bcbf375732c647488b3ce82a2fd7',
hex:
'4830450221008a7c86ce3a9f5765573440143a00631bb5afa9a58b24674a7fa794bb11ffffc30220397dd4c672c3e0dbe0c4eb6390b5727c5fdc3a42cf3ae275ac13b44d27bb234f412102bc9fc26ae2bd9a9a14e9baeda4b4fdd95a00bcbf375732c647488b3ce82a2fd7'
}, },
sequence: 4294967295 sequence: 4294967295
} }
@@ -144,36 +26,157 @@ const mockNoSendTx = {
value: 0.000006, value: 0.000006,
n: 0, n: 0,
scriptPubKey: { scriptPubKey: {
asm: 'OP_DUP OP_HASH160 3c79ba54317c4be9cbfa5aaf357f74b97f3d9696 OP_EQUALVERIFY OP_CHECKSIG', asm:
'OP_DUP OP_HASH160 3c79ba54317c4be9cbfa5aaf357f74b97f3d9696 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a9143c79ba54317c4be9cbfa5aaf357f74b97f3d969688ac', hex: '76a9143c79ba54317c4be9cbfa5aaf357f74b97f3d969688ac',
reqSigs: 1, reqSigs: 1,
type: 'pubkeyhash', type: 'pubkeyhash',
addresses: [ addresses: ['bitcoincash:qq78nwj5x97yh6wtlfd27dtlwjuh70vkjc59h8tgtg']
'bitcoincash:qq78nwj5x97yh6wtlfd27dtlwjuh70vkjc59h8tgtg'
]
} }
}, },
{ {
value: 0.00003664, value: 0.00003664,
n: 1, n: 1,
scriptPubKey: { scriptPubKey: {
asm: 'OP_DUP OP_HASH160 58502a73888e0a386fc69b44850af4b2a86ac9a6 OP_EQUALVERIFY OP_CHECKSIG', asm:
'OP_DUP OP_HASH160 58502a73888e0a386fc69b44850af4b2a86ac9a6 OP_EQUALVERIFY OP_CHECKSIG',
hex: '76a91458502a73888e0a386fc69b44850af4b2a86ac9a688ac', hex: '76a91458502a73888e0a386fc69b44850af4b2a86ac9a688ac',
reqSigs: 1, reqSigs: 1,
type: 'pubkeyhash', type: 'pubkeyhash',
addresses: [ addresses: ['bitcoincash:qpv9q2nn3z8q5wr0c6d5fpg27je2s6kf5cavp4ney0']
'bitcoincash:qpv9q2nn3z8q5wr0c6d5fpg27je2s6kf5cavp4ney0'
]
} }
} }
], ],
hex: '020000000189b17fd719713a2e33ee2c4f559c02ebd740a9b890d4cb787e5a9792d3c11c68010000006b4830450221008a7c86ce3a9f5765573440143a00631bb5afa9a58b24674a7fa794bb11ffffc30220397dd4c672c3e0dbe0c4eb6390b5727c5fdc3a42cf3ae275ac13b44d27bb234f412102bc9fc26ae2bd9a9a14e9baeda4b4fdd95a00bcbf375732c647488b3ce82a2fd7ffffffff0258020000000000001976a9143c79ba54317c4be9cbfa5aaf357f74b97f3d969688ac500e0000000000001976a91458502a73888e0a386fc69b44850af4b2a86ac9a688ac00000000' hex:
'020000000189b17fd719713a2e33ee2c4f559c02ebd740a9b890d4cb787e5a9792d3c11c68010000006b4830450221008a7c86ce3a9f5765573440143a00631bb5afa9a58b24674a7fa794bb11ffffc30220397dd4c672c3e0dbe0c4eb6390b5727c5fdc3a42cf3ae275ac13b44d27bb234f412102bc9fc26ae2bd9a9a14e9baeda4b4fdd95a00bcbf375732c647488b3ce82a2fd7ffffffff0258020000000000001976a9143c79ba54317c4be9cbfa5aaf357f74b97f3d969688ac500e0000000000001976a91458502a73888e0a386fc69b44850af4b2a86ac9a688ac00000000'
}
const mockFulcrumTxHistory = {
success: true,
transactions: [
{
height: 511463,
tx_hash:
'eff00a9538487ff44243c75fb13de19b5783454c42c81b9aff9afbfd09cbaec3'
},
{
height: 511464,
tx_hash:
'7e9aa7a74de2b30200a2d6fc748ff35a0c753221444194f720bb7f61ef1d9153'
},
{
height: 513373,
tx_hash:
'6960255abe64893073921e96bf3c053c82686e0fc22a565494fbe2a31e766975'
},
{
height: 513373,
tx_hash:
'9ea667bcfc9cd337bd6c5583d8094c1b1942bd2015d95b54189deac5070eeff0'
},
{
height: 560481,
tx_hash:
'ecc1b51bac767880382bf3190ff17abf78d0936843a022a943d871116ed50368'
},
{
height: 560615,
tx_hash:
'b3792d28377b975560e1b6f09e48aeff8438d4c6969ca578bd406393bd50bd7d'
},
{
height: 561568,
tx_hash:
'8bc2134c7e48e56e1769b3d7c4c1e3a0acc68e1e58160eee6fa67f3208c07262'
},
{
height: 561569,
tx_hash:
'ceb0cab0e37b59caf3ca29e1a698d19ff47f2827dd09cb2f3b91b9100b1dad1c'
},
{
height: 561572,
tx_hash:
'0f9b49cafeb9ae1d741cdb12137c92816aa8470944c270a78ba2e610bd59190d'
},
{
height: 561582,
tx_hash:
'e4a0ac48ff3f42fc342717a2a3d34248e5e85bae79d59bd20e1b60e61b1c500f'
},
{
height: 562106,
tx_hash:
'1afcc63b244182647909539ebe3f4a44b8ea4120a95edb8d9eebe5347b9491bb'
},
{
height: 562106,
tx_hash:
'c42f8f16d3baa2ee343ea89ef110dfe094992379d08edd30887b8ca7ee671c9a'
}
]
}
const mockTxDetails2 = {
txid: '7e9aa7a74de2b30200a2d6fc748ff35a0c753221444194f720bb7f61ef1d9153',
hash: '7e9aa7a74de2b30200a2d6fc748ff35a0c753221444194f720bb7f61ef1d9153',
version: 1,
size: 221,
locktime: 0,
vin: [
{
txid: 'eff00a9538487ff44243c75fb13de19b5783454c42c81b9aff9afbfd09cbaec3',
vout: 0,
scriptSig: {
asm:
'30440220150e7c9b646fe1f7f576456c9808c89a710e8fbb753e9f5ca6ed2d39a0b69ba602202212968163d8dfe35eec5975fce38440082029cb2691a3e0ad1714f9c412c47a[ALL|FORKID] 044eb40b025df18409f2a5197b010dd62a9e65d9a74e415e5b10367721a9c4baa7ebfee22d14b8ece1c9bd70c0d9e5e8b00b61b81b88a1b5ce6f24eac6b8a34b2c',
hex:
'4730440220150e7c9b646fe1f7f576456c9808c89a710e8fbb753e9f5ca6ed2d39a0b69ba602202212968163d8dfe35eec5975fce38440082029cb2691a3e0ad1714f9c412c47a4141044eb40b025df18409f2a5197b010dd62a9e65d9a74e415e5b10367721a9c4baa7ebfee22d14b8ece1c9bd70c0d9e5e8b00b61b81b88a1b5ce6f24eac6b8a34b2c'
},
sequence: 4294967295
}
],
vout: [
{
value: 0.01266023,
n: 0,
scriptPubKey: {
asm: 'OP_HASH160 91bacd6785e3f8c78d5f29b4020b94c753444130 OP_EQUAL',
hex: 'a91491bacd6785e3f8c78d5f29b4020b94c75344413087',
reqSigs: 1,
type: 'scripthash',
addresses: ['bitcoincash:pzgm4nt8sh3l33udtu5mgqstjnr4x3zpxqrrrndnw5']
}
}
],
hex:
'0100000001c3aecb09fdfb9aff9a1bc8424c4583579be13db15fc74342f47f4838950af0ef000000008a4730440220150e7c9b646fe1f7f576456c9808c89a710e8fbb753e9f5ca6ed2d39a0b69ba602202212968163d8dfe35eec5975fce38440082029cb2691a3e0ad1714f9c412c47a4141044eb40b025df18409f2a5197b010dd62a9e65d9a74e415e5b10367721a9c4baa7ebfee22d14b8ece1c9bd70c0d9e5e8b00b61b81b88a1b5ce6f24eac6b8a34b2cffffffff01675113000000000017a91491bacd6785e3f8c78d5f29b4020b94c7534441308700000000',
blockhash: '000000000000000001aab6818b4ad0379a3f7a13940d9b3d4838b3dea1d0a083',
confirmations: 149222,
time: 1515088493,
blocktime: 1515088493
}
const mockFulcrumNoTxHistory = {
success: true,
transactions: []
}
const mockFulcrumNoSendBalance = {
success: true,
transactions: [
{
height: 633578,
tx_hash:
'a3b62cd4f4c56ba52139179db14bffd4ab22a2e077f3c62bd5cf0541bfcaf023'
}
]
} }
module.exports = { module.exports = {
mockBalance, mockNoSendTx,
mockTxDetails, mockFulcrumTxHistory,
mockNoTxHistory, mockTxDetails2,
mockNoSendBalance, mockFulcrumNoTxHistory,
mockNoSendTx mockFulcrumNoSendBalance
} }
+241
View File
@@ -0,0 +1,241 @@
/*
This library contains mocking data for running unit tests on the address route.
*/
'use strict'
const mockCoinbaseFeed = {
data: {
currency: 'BCH',
rates: {
AED: '918.11634',
AFN: '19208.6584998',
ALGO: '823.28722002635056355',
ALL: '26365.4673517',
AMD: '120379.9761886',
ANG: '448.6632494',
AOA: '162813.68075',
ARS: '19392.84240565',
ATOM: '45.3670932026499700525',
AUD: '354.4385981',
AWG: '449.91',
AZN: '425.539875',
BAL: '18.824215193276753072',
BAM: '415.45514235',
BAND: '40.7768732564399590085',
BAT: '1168.9711697428450012',
BBD: '499.9',
BCH: '1.0',
BDT: '21194.1523216',
BGN: '415.36691',
BHD: '94.2501462',
BIF: '483670.78174295',
BMD: '249.95',
BND: '339.227141',
BOB: '1725.8732563',
BRL: '1401.494645',
BSD: '249.95',
BSV: '1.567361207606150942625',
BTC: '0.021275',
BTN: '18323.91073475',
BWP: '2859.81217315',
BYN: '640.5913561',
BYR: '6405913.561',
BZD: '503.8107177',
CAD: '329.6765515',
CDF: '490377.5463717',
CGLD: '121.6805004503079241715',
CHF: '227.5939721',
CLF: '7.13432285',
CLP: '196860.63474705',
CNH: '1669.50978125',
CNY: '1670.21589',
COMP: '2.429765723728978241',
COP: '961722.29431455',
CRC: '150790.4338804',
CUC: '250.01273745',
CVE: '23620.275',
CZK: '5790.09175',
DAI: '247.842348666936355635',
DASH: '3.338230383973288798',
DJF: '44495.8985401',
DKK: '1580.608815',
DOP: '14597.4179324',
DZD: '32214.830745',
EEK: '3652.69206545',
EGP: '3924.839875',
EOS: '96.8423091824874092665',
ERN: '3749.25274945',
ETB: '9313.6508972',
ETC: '45.04821122825989111',
ETH: '0.658777328255340453815',
EUR: '212.33',
FJD: '534.9054975',
FKP: '193.01413945',
GBP: '193.115',
GEL: '807.3385',
GGP: '193.01413945',
GHS: '1453.70845015',
GIP: '193.01413945',
GMD: '12934.9125',
GNF: '2447970.79115335',
GTQ: '1944.576007',
GYD: '52260.2868518',
HKD: '1937.122498',
HNL: '6129.328889',
HRK: '1610.802775',
HTG: '15746.55080985',
HUF: '77512.6058777',
IDR: '3675351.03275',
ILS: '846.1232415',
IMP: '193.01413945',
INR: '18345.655135',
IQD: '298009.02802165',
ISK: '34745.5495',
JEP: '193.01413945',
JMD: '36409.6696094',
JOD: '177.21455',
JPY: '26360.601825',
KES: '27184.562',
KGS: '20308.0755724',
KHR: '1025880.7533059',
KMF: '105029.03124175',
KNC: '274.68542227594924615',
KRW: '284915.23080495',
KWD: '76.4532063',
KYD: '208.2943328',
KZT: '107014.42658325',
LAK: '2310183.71158185',
LBP: '378370.54450325',
LINK: '22.894634759817007489',
LKR: '46090.95971405',
LRC: '1457.8594342373869446',
LRD: '48852.72175115',
LSL: '4122.3008749',
LTC: '5.2062070401999589123',
LTL: '806.05000775',
LVL: '164.03243695',
LYD: '341.5926678',
MAD: '2298.47346335',
MDL: '4228.5806147',
MGA: '982179.7557538',
MKD: '13087.73767885',
MKR: '0.43764476902003812305',
MMK: '322679.7621378',
MNT: '709349.8756452',
MOP: '1995.2583685',
MRO: '89232.15',
MTL: '170.9003131',
MUR: '9974.21650765',
MVR: '3849.23',
MWK: '188665.67006785',
MXN: '5298.265135',
MYR: '1036.517655',
MZN: '18229.3548997',
NAD: '4134.173',
NGN: '95481.03472305',
NIO: '8698.6554209',
NMR: '8.624556609112112958',
NOK: '2334.203066',
NPR: '29318.1806909',
NZD: '379.2351378',
OMG: '74.464123456422320125',
OMR: '96.23549905',
OXT: '1033.27821413807369045',
PAB: '249.95',
PEN: '896.6071427',
PGK: '874.90073485',
PHP: '12136.85639315',
PKR: '40613.9575836',
PLN: '971.640633',
PYG: '1756126.21474795',
QAR: '910.1304375',
REN: '769.6689761354887733',
REP: '18.2912550311013566725',
REPV2: '18.1582473904586151205',
RON: '1035.54285',
RSD: '24943.76025',
RUB: '19402.093805',
RWF: '244237.3512583',
SAR: '937.65668115',
SBD: '2019.805958',
SCR: '4576.6749819',
SEK: '2207.698372',
SGD: '339.57582125',
SHP: '193.01413945',
SLL: '2490751.74625075',
SOS: '145044.39506805',
SRD: '3537.7923',
SSP: '32558.487',
STD: '5250063.0293496',
SVC: '2187.04525345',
SZL: '4118.75383445',
THB: '7800.314625',
TJS: '2579.48624955',
TMT: '874.825',
TND: '688.2998125',
TOP: '579.953986',
TRY: '1969.70598',
TTD: '1696.3476626',
TWD: '7179.16412995',
TZS: '579879.84308155',
UAH: '7090.3476468',
UGX: '935325.80066935',
UMA: '29.276720351390922145',
UNI: '78.76161966283282792',
USD: '249.95',
USDC: '249.95',
UYU: '10724.30446005',
UZS: '2592145.88136715',
VEF: '62109486.17813795',
VES: '113073216.80555545',
VND: '5792476.0820382',
VUV: '28582.2184128',
WST: '655.5403657',
XAF: '139318.05656485',
XAG: '10.233577875',
XAU: '0.1313462255',
XCD: '675.5023725',
XDR: '177.0940741',
XLM: '2935.3219224332811736',
XOF: '139318.05656485',
XPD: '0.1065011955',
XPF: '25344.75028595',
XPT: '0.290981792',
XRP: '1016.6768354687817832',
XTZ: '114.010080507218287957',
YER: '62574.97275195',
YFI: '0.0181099566723181033515',
ZAR: '4130.57372',
ZEC: '3.88966697790227161905',
ZMK: '1313006.15998725',
ZMW: '5048.6210738',
ZRX: '642.804422350408735165',
ZWL: '80483.9'
}
}
}
const mockCoinexFeed = {
code: 0,
data: {
date: 1605649499848,
ticker: {
vol: '26717.39062407',
low: '10.4000',
open: '11.8000',
high: '19.0029',
last: '18.5000',
buy: '18.0100',
buy_amount: '200.00000000',
sell: '18.5000',
sell_amount: '47.89580474'
}
},
message: 'OK'
}
module.exports = {
mockCoinbaseFeed,
mockCoinexFeed
}
+16 -8
View File
@@ -352,7 +352,8 @@ const mockTxHistory = [
e: { e: {
h: '7bdd586ebd1e5f3dfd5295ab6e896c48b25855ab77a72c035ce1e7aacc965c2d', h: '7bdd586ebd1e5f3dfd5295ab6e896c48b25855ab77a72c035ce1e7aacc965c2d',
i: 1, i: 1,
s: 'RzBEAiAsu5o9EnxgZChjxKygxhGLhlfxJJbQfd4PP/vp2od3fAIgPpMqg+Sp4YpiInQZ6weJwELq8LEIrHXJpueBz7yF5gRBIQLsyL3809mRphc/+tPAcTQO5bi0kqZRFKpKIHXE3qN8Rw==', s:
'RzBEAiAsu5o9EnxgZChjxKygxhGLhlfxJJbQfd4PP/vp2od3fAIgPpMqg+Sp4YpiInQZ6weJwELq8LEIrHXJpueBz7yF5gRBIQLsyL3809mRphc/+tPAcTQO5bi0kqZRFKpKIHXE3qN8Rw==',
a: 'simpleledger:qrrrpqmdkggpnw0czwg0jgcjd7yhu25jy5zxh2gqdq' a: 'simpleledger:qrrrpqmdkggpnw0czwg0jgcjd7yhu25jy5zxh2gqdq'
} }
}, },
@@ -361,7 +362,8 @@ const mockTxHistory = [
e: { e: {
h: 'df49feff24dc34a10a44a0cbd7c908d964801ec7218512c84574fbce698535f0', h: 'df49feff24dc34a10a44a0cbd7c908d964801ec7218512c84574fbce698535f0',
i: 3, i: 3,
s: 'SDBFAiEAhW3zbKTlPrOXD2E2oEcNof6vCMPGYIg8vOVSE9c8IakCIFLg/gxydG9eL8HMAzkScGElKWJRnfhVMpMHU3evNcrZQSEDRS7F+pSC8OxSldsT4FctJLZBU7f2+FDiE05ae1xqtN0=', s:
'SDBFAiEAhW3zbKTlPrOXD2E2oEcNof6vCMPGYIg8vOVSE9c8IakCIFLg/gxydG9eL8HMAzkScGElKWJRnfhVMpMHU3evNcrZQSEDRS7F+pSC8OxSldsT4FctJLZBU7f2+FDiE05ae1xqtN0=',
a: 'simpleledger:qpkpeqfslejw5pptzcy25h2jxhsc9k0vts43n26up0' a: 'simpleledger:qpkpeqfslejw5pptzcy25h2jxhsc9k0vts43n26up0'
} }
} }
@@ -371,7 +373,8 @@ const mockTxHistory = [
e: { e: {
v: 0, v: 0,
i: 0, i: 0,
s: 'agRTTFAAAQEEU0VORCA46XxdfTWFosvz+VgMgsozmF+csIRdTcziIMtwn5U4sAgAAAAAAJiWgAgAAAAAJHfU+g==' s:
'agRTTFAAAQEEU0VORCA46XxdfTWFosvz+VgMgsozmF+csIRdTcziIMtwn5U4sAgAAAAAAJiWgAgAAAAAJHfU+g=='
} }
}, },
{ {
@@ -402,7 +405,8 @@ const mockTxHistory = [
slp: { slp: {
detail: { detail: {
decimals: 8, decimals: 8,
tokenIdHex: '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0', tokenIdHex:
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
transactionType: 'SEND', transactionType: 'SEND',
versionType: 1, versionType: 1,
documentUri: 'psfoundation.cash', documentUri: 'psfoundation.cash',
@@ -439,7 +443,8 @@ const mockTxHistory = [
e: { e: {
h: 'e4e1e1f6b502cbd42f69b919634cea3e37fc8595e6ca800e0a7d8f6dcdfb249e', h: 'e4e1e1f6b502cbd42f69b919634cea3e37fc8595e6ca800e0a7d8f6dcdfb249e',
i: 3, i: 3,
s: 'SDBFAiEAjCGRmU28x24LMTwg5XqC+fLf3zGhTYCsSWpmF8Eshq0CIAJhxAKMbwKWC4ASmjWzplpno2ch+hGNUD6HNWOdbd4SQSECeRsZo5Fl29g0A9bfJo1E/WIdowWBsLblyxWnEB7ViFE=', s:
'SDBFAiEAjCGRmU28x24LMTwg5XqC+fLf3zGhTYCsSWpmF8Eshq0CIAJhxAKMbwKWC4ASmjWzplpno2ch+hGNUD6HNWOdbd4SQSECeRsZo5Fl29g0A9bfJo1E/WIdowWBsLblyxWnEB7ViFE=',
a: 'simpleledger:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qznkmw3s7' a: 'simpleledger:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qznkmw3s7'
} }
}, },
@@ -448,7 +453,8 @@ const mockTxHistory = [
e: { e: {
h: '438420345dd8b7fb4ea74aaf2e3090f44899bf3c71662243946a441de6dae720', h: '438420345dd8b7fb4ea74aaf2e3090f44899bf3c71662243946a441de6dae720',
i: 2, i: 2,
s: 'SDBFAiEAhmLYL4QY3mvZo3/i0XG9PJAYMruw3MaGOLJ3Z4si7hwCIAaaGwRnj/cZK3L/KKFBoMtItpGRZ10GowNN//vgn6PKQSECeRsZo5Fl29g0A9bfJo1E/WIdowWBsLblyxWnEB7ViFE=', s:
'SDBFAiEAhmLYL4QY3mvZo3/i0XG9PJAYMruw3MaGOLJ3Z4si7hwCIAaaGwRnj/cZK3L/KKFBoMtItpGRZ10GowNN//vgn6PKQSECeRsZo5Fl29g0A9bfJo1E/WIdowWBsLblyxWnEB7ViFE=',
a: 'simpleledger:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qznkmw3s7' a: 'simpleledger:qpvsg9vl9a5mlf37a7n3yce6pktdctn73qznkmw3s7'
} }
} }
@@ -458,7 +464,8 @@ const mockTxHistory = [
e: { e: {
v: 0, v: 0,
i: 0, i: 0,
s: 'agRTTFAAAQEEU0VORCCk+1wtoaoGTiUBikP5FlBABx2emEuhkMIip/WQU6+EsggAAAAAAA9CQAgAAAAXQnHEwA==' s:
'agRTTFAAAQEEU0VORCCk+1wtoaoGTiUBikP5FlBABx2emEuhkMIip/WQU6+EsggAAAAAAA9CQAgAAAAXQnHEwA=='
} }
}, },
{ {
@@ -489,7 +496,8 @@ const mockTxHistory = [
slp: { slp: {
detail: { detail: {
decimals: 2, decimals: 2,
tokenIdHex: 'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2', tokenIdHex:
'a4fb5c2da1aa064e25018a43f9165040071d9e984ba190c222a7f59053af84b2',
transactionType: 'SEND', transactionType: 'SEND',
versionType: 1, versionType: 1,
documentUri: 'troutsblog.com', documentUri: 'troutsblog.com',
+280
View File
@@ -0,0 +1,280 @@
/*
TESTS FOR THE PRICE.JS LIBRARY
This test file uses the environment variable TEST to switch between unit
and integration tests. By default, TEST is set to 'unit'. Set this variable
to 'integration' to run the tests against BCH mainnet.
*/
'use strict'
const chai = require('chai')
const assert = chai.assert
const sinon = require('sinon')
const Price = require('../../src/routes/v3/price')
let uut
// Mocking data.
const { mockReq, mockRes } = require('./mocks/express-mocks')
const mockData = require('./mocks/price-mock')
// Used for debugging.
const util = require('util')
util.inspect.defaultOptions = { depth: 1 }
describe('#PriceRouter', () => {
let req, res
let sandbox
before(() => {
// Set default environment variables for unit tests.
if (!process.env.TEST) process.env.TEST = 'unit'
})
// Setup the mocks before each test.
beforeEach(() => {
// Mock the req and res objects used by Express routes.
req = mockReq
res = mockRes
// Explicitly reset the parmas and body.
req.params = {}
req.body = {}
req.query = {}
sandbox = sinon.createSandbox()
uut = new Price()
})
afterEach(() => {
// Restore Sandbox
sandbox.restore()
})
after(() => {})
describe('#root', async () => {
// root route handler.
// const root = controlRoute.testableComponents.root
it('should respond to GET for base route', async () => {
const result = uut.root(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.equal(result.status, 'price', 'Returns static string')
})
})
describe('#getUSD', () => {
// const getNetworkInfo = controlRoute.testableComponents.getNetworkInfo
it('should throw 500 when network issues', async () => {
uut.priceUrl = 'http://fakeurl/api/'
await uut.getUSD(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.isAbove(
res.statusCode,
499,
'HTTP status code 500 or greater expected.'
)
// console.log(res)
assert.include(
res.output.error,
'Network error: Could not communicate with full node or other external service'
)
})
it('returns proper error when downstream service stalls', async () => {
// Mock the timeout error.
sandbox.stub(uut.axios, 'request').throws({ code: 'ECONNABORTED' })
const result = await uut.getUSD(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('returns proper error when downstream service is down', async () => {
// Mock the timeout error.
sandbox.stub(uut.axios, 'request').throws({ code: 'ECONNREFUSED' })
const result = await uut.getUSD(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('should get the USD price of BCH', async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === 'unit') {
sandbox
.stub(uut.axios, 'request')
.resolves({ data: mockData.mockCoinbaseFeed })
}
const result = await uut.getUSD(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.isNumber(result.usd)
})
})
describe('#getBCHRate', () => {
it('should throw 500 when network issues', async () => {
uut.priceUrl = 'http://fakeurl/api/'
await uut.getBCHRate(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.isAbove(
res.statusCode,
499,
'HTTP status code 500 or greater expected.'
)
// console.log(res)
assert.include(
res.output.error,
'Network error: Could not communicate with full node or other external service'
)
})
it('returns proper error when downstream service stalls', async () => {
// Mock the timeout error.
sandbox.stub(uut.axios, 'request').throws({ code: 'ECONNABORTED' })
const result = await uut.getBCHRate(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('returns proper error when downstream service is down', async () => {
// Mock the timeout error.
sandbox.stub(uut.axios, 'request').throws({ code: 'ECONNREFUSED' })
const result = await uut.getBCHRate(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('should get several rates for BCH', async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === 'unit') {
sandbox
.stub(uut.axios, 'request')
.resolves({ data: mockData.mockCoinbaseFeed })
}
const result = await uut.getBCHRate(req, res)
// console.log(`result: ${util.inspect(result)}`)
// assert.isNumber(result)
assert.property(result, 'USD')
assert.property(result, 'CAD')
})
})
describe('#errorHandler', () => {
it('should handle unexpected errors', () => {
sandbox.stub(uut.routeUtils, 'decodeError').returns({ msg: false })
const result = uut.errorHandler(new Error('test error'), res)
// console.log('result: ', result)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.property(result, 'error')
})
})
describe('#getBCHAUSD', () => {
// const getNetworkInfo = controlRoute.testableComponents.getNetworkInfo
it('should throw 500 when network issues', async () => {
uut.coinexPriceUrl = 'http://fakeurl/api/'
await uut.getBCHAUSD(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.isAbove(
res.statusCode,
499,
'HTTP status code 500 or greater expected.'
)
// console.log(res)
assert.include(
res.output.error,
'Network error: Could not communicate with full node or other external service'
)
})
it('returns proper error when downstream service stalls', async () => {
// Mock the timeout error.
sandbox.stub(uut.axios, 'request').throws({ code: 'ECONNABORTED' })
const result = await uut.getBCHAUSD(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('returns proper error when downstream service is down', async () => {
// Mock the timeout error.
sandbox.stub(uut.axios, 'request').throws({ code: 'ECONNREFUSED' })
const result = await uut.getBCHAUSD(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
it('should get the USD price of BCH', async () => {
// Mock the RPC call for unit tests.
if (process.env.TEST === 'unit') {
sandbox
.stub(uut.axios, 'request')
.resolves({ data: mockData.mockCoinexFeed })
}
const result = await uut.getBCHAUSD(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.isNumber(result.usd)
})
})
})
+17 -17
View File
@@ -105,14 +105,14 @@ describe('#route-ratelimits & jwt-auth', () => {
}) })
describe('#calcPoints', () => { describe('#calcPoints', () => {
it('should return 300 points for anonymous user', () => { it('should return 50 points for anonymous user', () => {
const result = rateLimits.calcPoints() const result = rateLimits.calcPoints()
// console.log(`result: ${result}`) // console.log(`result: ${result}`)
assert.equal(result, 300) assert.equal(result, 50)
}) })
it('should return 100 points for free tier requesting full node access', () => { it('should return 50 points for free tier requesting full node access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 10, apiLevel: 10,
resource: 'blockchain', resource: 'blockchain',
@@ -120,10 +120,10 @@ describe('#route-ratelimits & jwt-auth', () => {
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 100) assert.equal(result, 50)
}) })
it('should return 100 points for free tier requesting indexer access', () => { it('should return 50 points for free tier requesting indexer access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 10, apiLevel: 10,
resource: 'blockbook', resource: 'blockbook',
@@ -131,10 +131,10 @@ describe('#route-ratelimits & jwt-auth', () => {
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 100) assert.equal(result, 50)
}) })
it('should return 100 points for free tier requesting SLPDB access', () => { it('should return 50 points for free tier requesting SLPDB access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 10, apiLevel: 10,
resource: 'slp', resource: 'slp',
@@ -142,7 +142,7 @@ describe('#route-ratelimits & jwt-auth', () => {
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 100) assert.equal(result, 50)
}) })
it('should return 10 points for full node tier requesting full node access', () => { it('should return 10 points for full node tier requesting full node access', () => {
@@ -156,7 +156,7 @@ describe('#route-ratelimits & jwt-auth', () => {
assert.equal(result, 10) assert.equal(result, 10)
}) })
it('should return 100 points for full-node tier requesting indexer access', () => { it('should return 50 points for full-node tier requesting indexer access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 20, apiLevel: 20,
resource: 'blockbook', resource: 'blockbook',
@@ -164,10 +164,10 @@ describe('#route-ratelimits & jwt-auth', () => {
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 100) assert.equal(result, 50)
}) })
it('should return 100 points for full node tier requesting SLPDB access', () => { it('should return 50 points for full node tier requesting SLPDB access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 20, apiLevel: 20,
resource: 'slp', resource: 'slp',
@@ -175,7 +175,7 @@ describe('#route-ratelimits & jwt-auth', () => {
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 100) assert.equal(result, 50)
}) })
it('should return 10 point for indexer tier requesting full node access', () => { it('should return 10 point for indexer tier requesting full node access', () => {
@@ -200,7 +200,7 @@ describe('#route-ratelimits & jwt-auth', () => {
assert.equal(result, 10) assert.equal(result, 10)
}) })
it('should return 100 points for indexer tier requesting SLPDB access', () => { it('should return 50 points for indexer tier requesting SLPDB access', () => {
const jwtInfo = { const jwtInfo = {
apiLevel: 30, apiLevel: 30,
resource: 'slp', resource: 'slp',
@@ -208,7 +208,7 @@ describe('#route-ratelimits & jwt-auth', () => {
} }
const result = rateLimits.calcPoints(jwtInfo) const result = rateLimits.calcPoints(jwtInfo)
assert.equal(result, 100) assert.equal(result, 50)
}) })
it('should return 10 point for SLP tier requesting full node access', () => { it('should return 10 point for SLP tier requesting full node access', () => {
@@ -319,7 +319,7 @@ describe('#route-ratelimits & jwt-auth', () => {
) )
}) })
it('should trigger rate-limit for free tier after 10 RPM', async () => { it('should trigger rate-limit for free tier after 20 RPM', async () => {
// Create a new instance of the rate limit so we start with zeroed tracking. // Create a new instance of the rate limit so we start with zeroed tracking.
rateLimits = new RateLimits() rateLimits = new RateLimits()
@@ -338,7 +338,7 @@ describe('#route-ratelimits & jwt-auth', () => {
// Mock the call to the jwt library. // Mock the call to the jwt library.
sandbox.stub(rateLimits.jwt, 'verify').returns(jwtInfo) sandbox.stub(rateLimits.jwt, 'verify').returns(jwtInfo)
for (let i = 0; i < 12; i++) { for (let i = 0; i < 22; i++) {
next.reset() // reset the stubbed next() function. next.reset() // reset the stubbed next() function.
await rateLimits.rateLimitByResource(req, res, next) await rateLimits.rateLimitByResource(req, res, next)
@@ -442,7 +442,7 @@ describe('#route-ratelimits & jwt-auth', () => {
// Issues with token secret should treat incoming requests as anonymous // Issues with token secret should treat incoming requests as anonymous
// calls with 30 points or 3 RPM. // calls with 30 points or 3 RPM.
assert.equal(res.locals.pointsToConsume, 300) assert.equal(res.locals.pointsToConsume, 50)
}) })
}) })
}) })
+15 -4
View File
@@ -975,6 +975,7 @@ describe('#Raw-Transactions', () => {
// Restore the saved URL. // Restore the saved URL.
process.env.RPC_BASEURL = savedUrl2 process.env.RPC_BASEURL = savedUrl2
}) })
it('should submit hex encoded transaction', async () => { it('should submit hex encoded transaction', async () => {
// This is a difficult test to run as transaction hex is invalid after a // This is a difficult test to run as transaction hex is invalid after a
// block confirmation. So the unit tests simulates what the output 'should' // block confirmation. So the unit tests simulates what the output 'should'
@@ -1003,8 +1004,13 @@ describe('#Raw-Transactions', () => {
// Integration test // Integration test
} else { } else {
assert.hasAllKeys(result, ['error']) if (process.env.ISBCHN) {
assert.include(result.error, 'Missing inputs') assert.hasAllKeys(result, ['error'])
assert.include(result.error, 'Missing inputs')
} else {
assert.hasAllKeys(result, ['error'])
assert.include(result.error, 'bad-txns-inputs-missingorspent')
}
} }
}) })
}) })
@@ -1158,8 +1164,13 @@ describe('#Raw-Transactions', () => {
// Integration test // Integration test
} else { } else {
assert.hasAllKeys(result, ['error']) if (process.env.ISBCHN) {
assert.include(result.error, 'Missing inputs') assert.hasAllKeys(result, ['error'])
assert.include(result.error, 'Missing inputs')
} else {
assert.hasAllKeys(result, ['error'])
assert.include(result.error, 'bad-txns-inputs-missingorspent')
}
} }
}) })
}) })
+103
View File
@@ -362,6 +362,7 @@ describe('#SLP', () => {
assert.hasAllKeys(result, ['error']) assert.hasAllKeys(result, ['error'])
assert.include(result.error, 'Array too large') assert.include(result.error, 'Array too large')
}) })
it('returns proper error when downstream service stalls', async () => { it('returns proper error when downstream service stalls', async () => {
// Mock the timeout error. // Mock the timeout error.
sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNABORTED' }) sandbox.stub(slpRoute.axios, 'request').throws({ code: 'ECONNABORTED' })
@@ -462,6 +463,80 @@ describe('#SLP', () => {
}) })
}) })
describe('#validate2Single', () => {
it('should throw 400 if txid is empty', async () => {
req.params.txid = ''
const result = await slpRoute.validate2Single(req, res)
// console.log(`result: ${util.inspect(result)}`)
assert.hasAllKeys(result, ['error'])
assert.include(result.error, 'txid can not be empty')
})
it('should invalidate a known invalid TXID', async () => {
if (process.env.TEST === 'unit') {
// Mock to prevent live network connection.
sandbox
.stub(slpRoute.axios, 'request')
.resolves({ data: { isValid: false } })
}
const txid =
'f7e5199ef6669ad4d078093b3ad56e355b6ab84567e59ad0f08a5ad0244f783a'
req.params.txid = txid
const result = await slpRoute.validate2Single(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result.txid, txid)
assert.equal(result.isValid, false)
})
it('should validate a known valid TXID', async () => {
if (process.env.TEST === 'unit') {
// Mock to prevent live network connection.
sandbox
.stub(slpRoute.axios, 'request')
.resolves({ data: { isValid: true } })
}
const txid =
'3a4b628cbcc183ab376d44ce5252325f042268307ffa4a53443e92b6d24fb488'
req.params.txid = txid
const result = await slpRoute.validate2Single(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.equal(result.txid, txid)
assert.equal(result.isValid, true)
})
// This test can only be run as a mocked unit test. It's too inconsistent
// to run as an integration test, due to the caching built into slp-validate.
if (process.env.TEST === 'unit') {
it('should cancel if validation takes too long', async () => {
// Mock the timeout error.
sandbox.stub(slpRoute.axios, 'request').throws({
code: 'ECONNABORTED'
})
const txid =
'eacb1085dfa296fef6d4ae2c0f4529a1bef096dd2325bdcc6dcb5241b3bdb579'
req.params.txid = txid
const result = await slpRoute.validate2Single(req, res)
// console.log(`result: ${JSON.stringify(result, null, 2)}`)
assert.isAbove(res.statusCode, 499, 'HTTP status code 503 expected.')
assert.include(
result.error,
'Could not communicate with full node',
'Error message expected'
)
})
}
})
describe('tokenStats()', () => { describe('tokenStats()', () => {
it('should throw 400 if tokenID is empty', async () => { it('should throw 400 if tokenID is empty', async () => {
req.params.tokenId = '' req.params.tokenId = ''
@@ -1034,6 +1109,34 @@ describe('#SLP', () => {
assert.property(result.slpUtxos[0].utxos[1], 'tokenType') assert.property(result.slpUtxos[0].utxos[1], 'tokenType')
assert.property(result.slpUtxos[0].utxos[1], 'tokenQty') 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'
)
})
}) })
}) })