mirror of
https://github.com/fullstack-cash/bch-api.git
synced 2026-09-22 09:12:05 -07:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
785d3059c3 | ||
|
|
21b19b59c9 | ||
|
|
588feff5db | ||
|
|
9fafc6fba3 | ||
|
|
8412da15db | ||
|
|
54c299e7fe | ||
|
|
a06399cfda | ||
|
|
e49c488e06 | ||
|
|
be07ffab33 | ||
|
|
41a5b24faf | ||
|
|
2172d0feff | ||
|
|
c4664ca173 | ||
|
|
11a7915cb8 | ||
|
|
06b944f36a | ||
|
|
9a8886121d | ||
|
|
89c6d48dfd | ||
|
|
4525b99f33 | ||
|
|
3d04507948 | ||
|
|
7e3a447973 | ||
|
|
bdde3a271d | ||
|
|
9450c3969b |
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2020 Chris Troutner
|
Copyright (c) 2021 Permissionless Software Foundation
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
# 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.
|
[](https://github.com/Permissionless-Software-Foundation/bch-js/blob/master/LICENSE.md)
|
||||||
|
[](https://github.com/feross/standard)
|
||||||
|
|
||||||
|
This is a REST API server, written in node.js JavaScript, using [Express.js](https://expressjs.com/) framework. 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://psfoundation.cash/blog/cash-stack) 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.
|
||||||
|
|
||||||
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 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.
|
||||||
|
|
||||||
@@ -20,7 +23,7 @@ The following features set this repository apart from rest.bitcoin.com:
|
|||||||
- 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). Can be used to monetize access to the REST API.
|
[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.
|
||||||
- Typescript removed and ES8 JavaScript used instead.
|
- Typescript removed and ES8 JavaScript used instead.
|
||||||
- npm audit run on all dependencies.
|
- `npm audit fix` frequently run to fix dependencies.
|
||||||
|
|
||||||
## 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
|
||||||
@@ -31,17 +34,17 @@ You can bootstrap your own REST API server by downloading and installing the inf
|
|||||||
### Configure bch-js
|
### Configure bch-js
|
||||||
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:
|
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/
|
- BCHN Mainnet REST API server: https://bchn.fullstack.cash/v4/
|
||||||
- ABC Mainnet REST API server: https://abc.fullstack.cash/v3/
|
- ABC Mainnet REST API server: https://abc.fullstack.cash/v4/
|
||||||
- Testnet3 REST API server: https://testnet3.fullstack.cash/v3/
|
- Testnet3 REST API server: https://testnet3.fullstack.cash/v4/
|
||||||
- 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 on [this page](https://troutsblog.com/research/bitcoin-cash/how-to-bch-full-stack-developer).
|
described on [this page](https://psfoundation.cash/blog/cash-stack).
|
||||||
|
|
||||||
This code targets the Ubuntu 18.04 LTS Linux OS.
|
This code targets the Ubuntu 18.04 LTS Linux OS or higher.
|
||||||
|
|
||||||
### Development
|
### Development
|
||||||
This is a standard node.js project. The installation is as follows:
|
This is a standard node.js project. The installation is as follows:
|
||||||
@@ -86,7 +89,7 @@ Have questions? Need help? Join our community support
|
|||||||
## IPFS
|
## IPFS
|
||||||
Copies of this repository will occasionally be uploaded and hosted on [IPFS](https://ipfs.io):
|
Copies of this repository will occasionally be uploaded and hosted on [IPFS](https://ipfs.io):
|
||||||
|
|
||||||
- v1.15.4: QmSAerFmDJm6KQp2HC8jsRR5MdL5X39hoUsJYvWNDvrD8m
|
- v1.25.1: [QmXFzFJHenymhReDc9oGAxbpmvK2fJYnisQ2pJn3gGyKKX](https://ipfs.io/ipfs/QmXFzFJHenymhReDc9oGAxbpmvK2fJYnisQ2pJn3gGyKKX)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
[MIT](./LICENSE.md)
|
[MIT](./LICENSE.md)
|
||||||
|
|||||||
+2
-2
@@ -10,9 +10,9 @@ const config = {
|
|||||||
: 'secret-jwt-token',
|
: 'secret-jwt-token',
|
||||||
|
|
||||||
// Rate Limits
|
// Rate Limits
|
||||||
anonRateLimit: process.env.ANON_RATE_LIMIT ? process.env.ANON_RATE_LIMIT : 50,
|
anonRateLimit: process.env.ANON_RATE_LIMIT ? Number(process.env.ANON_RATE_LIMIT) : 50,
|
||||||
whitelistRateLimit: process.env.WHITELIST_RATE_LIMIT
|
whitelistRateLimit: process.env.WHITELIST_RATE_LIMIT
|
||||||
? process.env.WHITELIST_RATE_LIMIT
|
? Number(process.env.WHITELIST_RATE_LIMIT)
|
||||||
: 10,
|
: 10,
|
||||||
whitelistDomains: process.env.WHITELIST_DOMAINS
|
whitelistDomains: process.env.WHITELIST_DOMAINS
|
||||||
? process.env.WHITELIST_DOMAINS.split(',')
|
? process.env.WHITELIST_DOMAINS.split(',')
|
||||||
|
|||||||
@@ -1,21 +1,28 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# test
|
# Docker containers for the below infrastructure is described here:
|
||||||
|
# https://psfoundation.cash/blog/cash-stack
|
||||||
|
# Pre-synced databases can be downloaded on the CashStrap page:
|
||||||
|
# https://fullstack.cash/cashstrap
|
||||||
|
|
||||||
|
# Which network are you using?
|
||||||
|
export NETWORK=mainnet
|
||||||
|
#export NETWORK=testnet
|
||||||
|
|
||||||
# Full node
|
# Full node
|
||||||
export RPC_BASEURL=http://172.17.0.1:8332/
|
export RPC_IP=172.17.0.1:8332
|
||||||
|
export RPC_BASEURL=http://$RPC_IP/
|
||||||
export RPC_USERNAME=bitcoin
|
export RPC_USERNAME=bitcoin
|
||||||
export RPC_PASSWORD=password
|
export RPC_PASSWORD=password
|
||||||
export NETWORK=mainnet
|
|
||||||
|
|
||||||
# SLPDB
|
# SLPDB
|
||||||
export SLPDB_URL=http://172.17.0.1:12300/
|
export SLPDB_URL=http://172.17.0.1:12300/
|
||||||
export SLPDB_PASS=somelongpassword
|
export SLPDB_PASS=somelongpassword
|
||||||
|
# Use the same address as SLPDB_URL if you don't have a separate whitelist server.
|
||||||
# Blockbook Indexer
|
export SLPDB_WHITELIST_URL=http://172.17.0.1:12300/
|
||||||
export BLOCKBOOK_URL=https://172.17.0.1:9131/
|
# slp-api alternative SLP validator using slp-validate:
|
||||||
# Allow node.js to make network calls to https using self-signed certificate.
|
# https://github.com/Permissionless-Software-Foundation/slp-api
|
||||||
export NODE_TLS_REJECT_UNAUTHORIZED=0
|
export SLP_API_URL=http://10.0.0.5:5001/
|
||||||
|
|
||||||
# Mainnet Fulcrum / ElectrumX
|
# Mainnet Fulcrum / ElectrumX
|
||||||
export FULCRUM_URL=172.17.0.1
|
export FULCRUM_URL=172.17.0.1
|
||||||
@@ -26,12 +33,25 @@ export REDIS_PORT=6379
|
|||||||
export REDIS_HOST=172.17.0.1
|
export REDIS_HOST=172.17.0.1
|
||||||
|
|
||||||
# JWT Token Secret
|
# JWT Token Secret
|
||||||
|
# This is used to verify JWT tokens generated with jwt-bch-api:
|
||||||
|
# https://github.com/Permissionless-Software-Foundation/jwt-bch-api
|
||||||
export TOKENSECRET=somelongsecretvalue
|
export TOKENSECRET=somelongsecretvalue
|
||||||
|
|
||||||
# So that bch-api can call bch-js locally.
|
# So that bch-api can call bch-js locally.
|
||||||
export LOCAL_RESTURL=http://127.0.0.1:3000/v3/
|
export LOCAL_RESTURL=http://127.0.0.1:3000/v4/
|
||||||
|
|
||||||
# slp-api alternative SLP validator.
|
# Basic Authentication password
|
||||||
export SLP_API_URL=http://10.0.0.5:5001/
|
export PRO_PASS=somerandomepassword:someotherrandompassword:aThirdPassword
|
||||||
|
|
||||||
|
# Whitelisted domains. Automatically give pro-tier rate limit access to apps
|
||||||
|
# that originate froma domain on the whitelist.
|
||||||
|
export WHITELIST_DOMAINS=fullstack.cash,psfoundation.cash,torlist.cash
|
||||||
|
|
||||||
|
# Rate Limits. Numbers are divided into 1000. e.g. 1000 / 50 = 20 RPM for ANON.
|
||||||
|
# Requests use the ANON rate limit if they fail to pass in a JWT token.
|
||||||
|
# ANON = 20 requests per minute (RPM)
|
||||||
|
export ANON_RATE_LIMIT=50
|
||||||
|
# 10 = 100 RPM
|
||||||
|
export WHITELIST_RATE_LIMIT=10
|
||||||
|
|
||||||
npm start
|
npm start
|
||||||
|
|||||||
@@ -1,22 +1,32 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Docker containers for the below infrastructure is described here:
|
||||||
|
# https://psfoundation.cash/blog/cash-stack
|
||||||
|
# Pre-synced databases can be downloaded on the CashStrap page:
|
||||||
|
# https://fullstack.cash/cashstrap
|
||||||
|
|
||||||
|
# Which network are you using?
|
||||||
|
#export NETWORK=mainnet
|
||||||
|
export NETWORK=testnet
|
||||||
|
|
||||||
# Full node
|
# Full node
|
||||||
export RPC_BASEURL=http://172.17.0.1:18332/
|
export RPC_IP=172.17.0.1:18332
|
||||||
|
export RPC_BASEURL=http://$RPC_IP/
|
||||||
export RPC_USERNAME=bitcoin
|
export RPC_USERNAME=bitcoin
|
||||||
export RPC_PASSWORD=password
|
export RPC_PASSWORD=password
|
||||||
export NETWORK=testnet
|
|
||||||
|
|
||||||
# SLPDB
|
# SLPDB
|
||||||
export SLPDB_URL=http://172.17.0.1:13300/
|
export SLPDB_URL=http://172.17.0.1:13300/
|
||||||
|
export SLPDB_PASS=somelongpassword
|
||||||
# Blockbook Indexer
|
# Use the same address as SLPDB_URL if you don't have a separate whitelist server.
|
||||||
export BLOCKBOOK_URL=https://172.17.0.1:19131/
|
export SLPDB_WHITELIST_URL=http://172.17.0.1:13300/
|
||||||
# Allow node.js to make network calls to https using self-signed certificate.
|
# slp-api alternative SLP validator using slp-validate:
|
||||||
export NODE_TLS_REJECT_UNAUTHORIZED=0
|
# https://github.com/Permissionless-Software-Foundation/slp-api
|
||||||
|
export SLP_API_URL=http://10.0.0.5:5001/
|
||||||
|
|
||||||
# Mainnet Fulcrum / ElectrumX
|
# Mainnet Fulcrum / ElectrumX
|
||||||
export FULCRUM_URL=172.17.0.1
|
export FULCRUM_URL=172.17.0.1
|
||||||
export FULCRUM_PORT=50002
|
export FULCRUM_PORT=60002
|
||||||
|
|
||||||
# Redis DB
|
# Redis DB
|
||||||
export REDIS_PORT=6380
|
export REDIS_PORT=6380
|
||||||
@@ -26,9 +36,20 @@ export REDIS_HOST=172.17.0.1
|
|||||||
export TOKENSECRET=somelongsecretvalue
|
export TOKENSECRET=somelongsecretvalue
|
||||||
|
|
||||||
# So that bch-api can call bch-js locally.
|
# So that bch-api can call bch-js locally.
|
||||||
export LOCAL_RESTURL=http://127.0.0.1:3000/v3/
|
export LOCAL_RESTURL=http://127.0.0.1:3000/v4/
|
||||||
|
|
||||||
# slp-api alternative SLP validator.
|
# Basic Authentication password (optional)
|
||||||
export SLP_API_URL=http://10.0.0.5:5001/
|
export PRO_PASS=somerandomepassword:someotherrandompassword:aThirdPassword
|
||||||
|
|
||||||
|
# Whitelisted domains. Automatically give pro-tier rate limit access to apps
|
||||||
|
# that originate froma domain on the whitelist.
|
||||||
|
export WHITELIST_DOMAINS=fullstack.cash,psfoundation.cash,torlist.cash
|
||||||
|
|
||||||
|
# Rate Limits. Numbers are divided into 1000. e.g. 1000 / 50 = 20 RPM for ANON.
|
||||||
|
# Requests use the ANON rate limit if they fail to pass in a JWT token.
|
||||||
|
# ANON = 20 requests per minute (RPM)
|
||||||
|
export ANON_RATE_LIMIT=50
|
||||||
|
# 10 = 100 RPM
|
||||||
|
export WHITELIST_RATE_LIMIT=10
|
||||||
|
|
||||||
npm start
|
npm start
|
||||||
|
|||||||
Generated
+3030
-4950
File diff suppressed because it is too large
Load Diff
+7
-15
@@ -22,16 +22,16 @@
|
|||||||
"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/v4/",
|
"coverage:report": "export NETWORK=mainnet && nyc --reporter=html mocha --timeout 25000 test/v4/",
|
||||||
"docs": "./node_modules/.bin/apidoc -i src/routes/v4 -o docs",
|
"docs": "./node_modules/.bin/apidoc -i src/routes/v4 -o docs",
|
||||||
"test:temp1": "export NETWORK=mainnet && export TEST=integration && mocha --timeout 25000 test/v4/integration/price.js",
|
"test:temp1": "export NETWORK=mainnet && export TEST=integration && mocha --timeout 25000 -g '#generateSendOpReturn()' test/v4/integration/",
|
||||||
"test:temp2": "mocha test/v4/rate-limits.js"
|
"test:temp2": "mocha test/v4/rate-limits.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.15.1"
|
"node": ">=10.15.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@psf/bch-js": "^4.2.5",
|
"@psf/bch-js": "^4.5.4",
|
||||||
"apidoc": "^0.23.0",
|
"apidoc": "^0.26.0",
|
||||||
"axios": "^0.19.0",
|
"axios": "^0.21.1",
|
||||||
"bitcore-lib-cash": "^8.23.1",
|
"bitcore-lib-cash": "^8.23.1",
|
||||||
"body-parser": "^1.18.3",
|
"body-parser": "^1.18.3",
|
||||||
"cookie-parser": "~1.4.3",
|
"cookie-parser": "~1.4.3",
|
||||||
@@ -45,7 +45,6 @@
|
|||||||
"helmet": "^3.21.2",
|
"helmet": "^3.21.2",
|
||||||
"ioredis": "^4.14.1",
|
"ioredis": "^4.14.1",
|
||||||
"jsonwebtoken": "^8.5.1",
|
"jsonwebtoken": "^8.5.1",
|
||||||
"level": "^6.0.0",
|
|
||||||
"mkdirp": "^1.0.0",
|
"mkdirp": "^1.0.0",
|
||||||
"mocha": "^7.1.1",
|
"mocha": "^7.1.1",
|
||||||
"morgan": "^1.9.1",
|
"morgan": "^1.9.1",
|
||||||
@@ -53,8 +52,6 @@
|
|||||||
"passport": "^0.4.0",
|
"passport": "^0.4.0",
|
||||||
"passport-anonymous": "^1.0.1",
|
"passport-anonymous": "^1.0.1",
|
||||||
"passport-http": "^0.3.0",
|
"passport-http": "^0.3.0",
|
||||||
"pg": "^8.0.0",
|
|
||||||
"pg-hstore": "^2.3.2",
|
|
||||||
"rate-limiter-flexible": "^2.0.0",
|
"rate-limiter-flexible": "^2.0.0",
|
||||||
"winston": "^3.2.1",
|
"winston": "^3.2.1",
|
||||||
"winston-daily-rotate-file": "^4.0.0"
|
"winston-daily-rotate-file": "^4.0.0"
|
||||||
@@ -69,17 +66,12 @@
|
|||||||
"eslint-plugin-prettier": "^3.1.0",
|
"eslint-plugin-prettier": "^3.1.0",
|
||||||
"eslint-plugin-standard": "^4.0.0",
|
"eslint-plugin-standard": "^4.0.0",
|
||||||
"fs-extra": "^9.0.0",
|
"fs-extra": "^9.0.0",
|
||||||
"nock": "^12.0.0",
|
"nock": "^13.0.5",
|
||||||
"node-mocks-http": "^1.7.0",
|
|
||||||
"nyc": "^15.0.0",
|
"nyc": "^15.0.0",
|
||||||
"prettier": "^2.0.0",
|
"prettier": "^2.0.0",
|
||||||
"proxyquire": "^2.1.0",
|
"semantic-release": "^17.3.1",
|
||||||
"request": "^2.88.0",
|
|
||||||
"request-promise": "^4.2.2",
|
|
||||||
"semantic-release": "^17.3.0",
|
|
||||||
"sinon": "^9.0.0",
|
"sinon": "^9.0.0",
|
||||||
"standard": "^14.3.1",
|
"standard": "^14.3.1"
|
||||||
"typescript": "^3.1.4"
|
|
||||||
},
|
},
|
||||||
"release": {
|
"release": {
|
||||||
"publish": [
|
"publish": [
|
||||||
|
|||||||
+2
-2
@@ -262,11 +262,11 @@ function onError (error) {
|
|||||||
case 'EACCES':
|
case 'EACCES':
|
||||||
console.error(`${bind} requires elevated privileges`)
|
console.error(`${bind} requires elevated privileges`)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
break
|
// break
|
||||||
case 'EADDRINUSE':
|
case 'EADDRINUSE':
|
||||||
console.error(`${bind} is already in use`)
|
console.error(`${bind} is already in use`)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
break
|
// break
|
||||||
default:
|
default:
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ class Electrum {
|
|||||||
const { msg, status } = _this.routeUtils.decodeError(err)
|
const { msg, status } = _this.routeUtils.decodeError(err)
|
||||||
if (msg) {
|
if (msg) {
|
||||||
res.status(status)
|
res.status(status)
|
||||||
return res.json({ error: msg })
|
return res.json({ success: false, error: msg })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle error patterns specific to this route.
|
// Handle error patterns specific to this route.
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ class Encryption {
|
|||||||
const { msg, status } = _this.routeUtils.decodeError(err)
|
const { msg, status } = _this.routeUtils.decodeError(err)
|
||||||
if (msg) {
|
if (msg) {
|
||||||
res.status(status)
|
res.status(status)
|
||||||
return res.json({ error: msg })
|
return res.json({ success: false, error: msg })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle error patterns specific to this route.
|
// Handle error patterns specific to this route.
|
||||||
|
|||||||
@@ -175,8 +175,17 @@ function decodeError (err) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle general Error objects.
|
||||||
|
if (err.message) {
|
||||||
|
return {
|
||||||
|
message: err.message,
|
||||||
|
status: 422
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return { msg: false, status: 500 }
|
return { msg: false, status: 500 }
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error('unhandled error in route-utils.js/decodeError(): ', err)
|
||||||
wlogger.error('unhandled error in route-utils.js/decodeError(): ', err)
|
wlogger.error('unhandled error in route-utils.js/decodeError(): ', err)
|
||||||
return { msg: false, status: 500 }
|
return { msg: false, status: 500 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1896,6 +1896,8 @@ class Slp {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// console.log('sendQty: ', sendQty)
|
||||||
|
// console.log(`tokenUtxos: `, tokenUtxos)
|
||||||
const opReturn = await _this.bchjs.SLP.TokenType1.generateSendOpReturn(
|
const opReturn = await _this.bchjs.SLP.TokenType1.generateSendOpReturn(
|
||||||
tokenUtxos,
|
tokenUtxos,
|
||||||
sendQty
|
sendQty
|
||||||
@@ -1907,6 +1909,7 @@ class Slp {
|
|||||||
res.status(200)
|
res.status(200)
|
||||||
return res.json({ script, outputs: opReturn.outputs })
|
return res.json({ script, outputs: opReturn.outputs })
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.log('err: ', err)
|
||||||
wlogger.error('Error in slp.js/generateSendOpReturn().', err)
|
wlogger.error('Error in slp.js/generateSendOpReturn().', err)
|
||||||
|
|
||||||
// Decode the error message.
|
// Decode the error message.
|
||||||
@@ -1996,9 +1999,11 @@ class Slp {
|
|||||||
|
|
||||||
// Decode the error message.
|
// Decode the error message.
|
||||||
const { msg, status } = routeUtils.decodeError(err)
|
const { msg, status } = routeUtils.decodeError(err)
|
||||||
|
console.log('msg: ', msg)
|
||||||
|
console.log('status: ', status)
|
||||||
if (msg) {
|
if (msg) {
|
||||||
res.status(status)
|
res.status(status)
|
||||||
return res.json({ error: msg })
|
return res.json({ error: msg, message: msg, success: false })
|
||||||
}
|
}
|
||||||
|
|
||||||
res.status(500)
|
res.status(500)
|
||||||
|
|||||||
@@ -154,8 +154,17 @@ class RouteUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle general Error objects.
|
||||||
|
if (err.message) {
|
||||||
|
return {
|
||||||
|
msg: err.message,
|
||||||
|
status: 422
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return { msg: false, status: 500 }
|
return { msg: false, status: 500 }
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.error('unhandled error in route-utils.js/decodeError(): ', err)
|
||||||
wlogger.error('unhandled error in route-utils.js/decodeError(): ', err)
|
wlogger.error('unhandled error in route-utils.js/decodeError(): ', err)
|
||||||
return { msg: false, status: 500 }
|
return { msg: false, status: 500 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,19 @@ require('winston-daily-rotate-file')
|
|||||||
|
|
||||||
var NETWORK = process.env.NETWORK
|
var NETWORK = process.env.NETWORK
|
||||||
|
|
||||||
|
// Default 1 Megabyte
|
||||||
|
var LOG_MAX_SIZE = process.env.LOG_MAX_SIZE ? process.env.LOG_MAX_SIZE : '1m'
|
||||||
|
|
||||||
|
// Default 5 days.
|
||||||
|
var LOG_MAX_FILES = process.env.LOG_MAX_FILES ? process.env.LOG_MAX_FILES : '5d'
|
||||||
|
|
||||||
// Configure daily-rotation transport.
|
// Configure daily-rotation transport.
|
||||||
var transport = new winston.transports.DailyRotateFile({
|
var transport = new winston.transports.DailyRotateFile({
|
||||||
filename: `${__dirname}/../../logs/rest-${NETWORK}-%DATE%.log`,
|
filename: `${__dirname}/../../logs/rest-${NETWORK}-%DATE%.log`,
|
||||||
datePattern: 'YYYY-MM-DD',
|
datePattern: 'YYYY-MM-DD',
|
||||||
zippedArchive: false,
|
zippedArchive: false,
|
||||||
maxSize: '1m',
|
maxSize: LOG_MAX_SIZE,
|
||||||
maxFiles: '5d',
|
maxFiles: LOG_MAX_FILES,
|
||||||
format: winston.format.combine(
|
format: winston.format.combine(
|
||||||
winston.format.timestamp(),
|
winston.format.timestamp(),
|
||||||
winston.format.json()
|
winston.format.json()
|
||||||
|
|||||||
+38
-12
@@ -1,37 +1,63 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Full node
|
# Docker containers for the below infrastructure is described here:
|
||||||
export RPC_BASEURL=http://<full node ip>:8332/
|
# https://psfoundation.cash/blog/cash-stack
|
||||||
export RPC_USERNAME=<RPC username>
|
# Pre-synced databases can be downloaded on the CashStrap page:
|
||||||
export RPC_PASSWORD=<RPC password>
|
# https://fullstack.cash/cashstrap
|
||||||
|
|
||||||
|
# Which network are you using?
|
||||||
export NETWORK=mainnet
|
export NETWORK=mainnet
|
||||||
|
#export NETWORK=testnet
|
||||||
|
|
||||||
|
# Full node
|
||||||
|
export RPC_IP=<ip>:8332
|
||||||
|
export RPC_BASEURL=http://$RPC_IP/
|
||||||
|
export RPC_USERNAME=bitcoin
|
||||||
|
export RPC_PASSWORD=password
|
||||||
|
|
||||||
# SLPDB
|
# SLPDB
|
||||||
export SLPDB_URL=http://<SLPDB IP>:12300/
|
export SLPDB_URL=http://<SLPDB IP>:12300/
|
||||||
export SLPDB_PASS=somelongpassword
|
export SLPDB_PASS=somelongpassword
|
||||||
|
|
||||||
# Use the same address as SLPDB_URL if you don't have a separate whitelist server.
|
# Use the same address as SLPDB_URL if you don't have a separate whitelist server.
|
||||||
export SLPDB_WHITELIST_URL=http://<SLPDB IP>:12300/
|
export SLPDB_WHITELIST_URL=http://<SLPDB IP>:12300/
|
||||||
|
|
||||||
# slp-api alternative SLP validator using slp-validate:
|
# slp-api alternative SLP validator using slp-validate:
|
||||||
# https://github.com/Permissionless-Software-Foundation/slp-api
|
# https://github.com/Permissionless-Software-Foundation/slp-api
|
||||||
export SLP_API_URL=http://10.0.0.5:5001/
|
export SLP_API_URL=http://10.0.0.5:5001/
|
||||||
|
|
||||||
# Blockbook
|
|
||||||
export BLOCKBOOK_URL=https://<Blockbook IP>:9131/
|
|
||||||
# Allow node.js to make network calls to https using self-signed certificate.
|
|
||||||
export NODE_TLS_REJECT_UNAUTHORIZED=0
|
|
||||||
|
|
||||||
# Mainnet Fulcrum / ElectrumX
|
# Mainnet Fulcrum / ElectrumX
|
||||||
export FULCRUM_URL=192.168.0.6
|
export FULCRUM_URL=192.168.0.6
|
||||||
export FULCRUM_PORT=50002
|
export FULCRUM_PORT=50002
|
||||||
|
|
||||||
|
# Redis DB - Used for rate limiting - customize to your own Redis installation.
|
||||||
|
export REDIS_PORT=6379
|
||||||
|
export REDIS_HOST=172.17.0.1
|
||||||
|
|
||||||
|
# JWT Token Secret
|
||||||
|
# This is used to verify JWT tokens generated with jwt-bch-api:
|
||||||
|
# https://github.com/Permissionless-Software-Foundation/jwt-bch-api
|
||||||
export TOKENSECRET=somelongpassword
|
export TOKENSECRET=somelongpassword
|
||||||
|
|
||||||
# So that bch-api can call bch-js locally.
|
# So that bch-api can call bch-js locally.
|
||||||
export LOCAL_RESTURL=http://127.0.0.1:3000/v3/
|
export LOCAL_RESTURL=http://127.0.0.1:3000/v4/
|
||||||
|
|
||||||
# Basic Authentication password
|
# Basic Authentication password
|
||||||
export PRO_PASS=somerandomepassword:someotherrandompassword:aThirdPassword
|
export PRO_PASS=somerandomepassword:someotherrandompassword:aThirdPassword
|
||||||
|
|
||||||
|
# Whitelisted domains. Automatically give pro-tier rate limit access to apps
|
||||||
|
# that originate froma domain on the whitelist.
|
||||||
|
export WHITELIST_DOMAINS=fullstack.cash,psfoundation.cash,torlist.cash
|
||||||
|
|
||||||
|
# Rate Limits. Numbers are divided into 1000. e.g. 1000 / 50 = 20 RPM for ANON.
|
||||||
|
# Requests use the ANON rate limit if they fail to pass in a JWT token.
|
||||||
|
# ANON = 20 requests per minute (RPM)
|
||||||
|
export ANON_RATE_LIMIT=50
|
||||||
|
# 10 = 100 RPM
|
||||||
|
export WHITELIST_RATE_LIMIT=10
|
||||||
|
|
||||||
|
# Set logging parameters
|
||||||
|
#1m means no more than 1 megabyte
|
||||||
|
export LOG_MAX_SIZE=1m
|
||||||
|
#5d means store no more than 5 days
|
||||||
|
export LOG_MAX_FILES=5d
|
||||||
|
|
||||||
npm start
|
npm start
|
||||||
|
|||||||
@@ -199,11 +199,11 @@ describe('#Electrumx', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('#getUtxos', () => {
|
describe('#getUtxos', () => {
|
||||||
it('should throw 400 if address is empty', async () => {
|
it('should throw 422 if address is empty', async () => {
|
||||||
const result = await electrumxRoute.getUtxos(req, res)
|
const result = await electrumxRoute.getUtxos(req, res)
|
||||||
// console.log(`result: ${util.inspect(result)}`)
|
// console.log(`result: ${util.inspect(result)}`)
|
||||||
|
|
||||||
assert.equal(res.statusCode, 400, 'Expect 400 status code')
|
assert.equal(res.statusCode, 422, 'Expect 422 status code')
|
||||||
|
|
||||||
assert.property(result, 'error')
|
assert.property(result, 'error')
|
||||||
assert.include(result.error, 'Unsupported address format')
|
assert.include(result.error, 'Unsupported address format')
|
||||||
@@ -233,7 +233,7 @@ describe('#Electrumx', () => {
|
|||||||
const result = await electrumxRoute.getUtxos(req, res)
|
const result = await electrumxRoute.getUtxos(req, res)
|
||||||
// console.log(`result: ${util.inspect(result)}`)
|
// console.log(`result: ${util.inspect(result)}`)
|
||||||
|
|
||||||
assert.equal(res.statusCode, 400, 'Expect 400 status code')
|
assert.equal(res.statusCode, 422, 'Expect 422 status code')
|
||||||
|
|
||||||
assert.property(result, 'error')
|
assert.property(result, 'error')
|
||||||
assert.include(result.error, 'Unsupported address format')
|
assert.include(result.error, 'Unsupported address format')
|
||||||
@@ -1063,7 +1063,7 @@ describe('#Electrumx', () => {
|
|||||||
const result = await electrumxRoute.getBalance(req, res)
|
const result = await electrumxRoute.getBalance(req, res)
|
||||||
// console.log(`result: ${util.inspect(result)}`)
|
// console.log(`result: ${util.inspect(result)}`)
|
||||||
|
|
||||||
assert.equal(res.statusCode, 400, 'Expect 400 status code')
|
assert.equal(res.statusCode, 422, 'Expect 422 status code')
|
||||||
|
|
||||||
assert.property(result, 'error')
|
assert.property(result, 'error')
|
||||||
assert.include(result.error, 'Unsupported address format')
|
assert.include(result.error, 'Unsupported address format')
|
||||||
@@ -1093,7 +1093,7 @@ describe('#Electrumx', () => {
|
|||||||
const result = await electrumxRoute.getBalance(req, res)
|
const result = await electrumxRoute.getBalance(req, res)
|
||||||
// console.log(`result: ${util.inspect(result)}`)
|
// console.log(`result: ${util.inspect(result)}`)
|
||||||
|
|
||||||
assert.equal(res.statusCode, 400, 'Expect 400 status code')
|
assert.equal(res.statusCode, 422, 'Expect 422 status code')
|
||||||
|
|
||||||
assert.property(result, 'error')
|
assert.property(result, 'error')
|
||||||
assert.include(result.error, 'Unsupported address format')
|
assert.include(result.error, 'Unsupported address format')
|
||||||
@@ -1350,7 +1350,7 @@ describe('#Electrumx', () => {
|
|||||||
const result = await electrumxRoute.getTransactions(req, res)
|
const result = await electrumxRoute.getTransactions(req, res)
|
||||||
// console.log(`result: ${util.inspect(result)}`)
|
// console.log(`result: ${util.inspect(result)}`)
|
||||||
|
|
||||||
assert.equal(res.statusCode, 400, 'Expect 400 status code')
|
assert.equal(res.statusCode, 422, 'Expect 422 status code')
|
||||||
|
|
||||||
assert.property(result, 'error')
|
assert.property(result, 'error')
|
||||||
assert.include(result.error, 'Unsupported address format')
|
assert.include(result.error, 'Unsupported address format')
|
||||||
@@ -1380,7 +1380,7 @@ describe('#Electrumx', () => {
|
|||||||
const result = await electrumxRoute.getTransactions(req, res)
|
const result = await electrumxRoute.getTransactions(req, res)
|
||||||
// console.log(`result: ${util.inspect(result)}`)
|
// console.log(`result: ${util.inspect(result)}`)
|
||||||
|
|
||||||
assert.equal(res.statusCode, 400, 'Expect 400 status code')
|
assert.equal(res.statusCode, 422, 'Expect 422 status code')
|
||||||
|
|
||||||
assert.property(result, 'error')
|
assert.property(result, 'error')
|
||||||
assert.include(result.error, 'Unsupported address format')
|
assert.include(result.error, 'Unsupported address format')
|
||||||
@@ -1645,7 +1645,7 @@ describe('#Electrumx', () => {
|
|||||||
const result = await electrumxRoute.getMempool(req, res)
|
const result = await electrumxRoute.getMempool(req, res)
|
||||||
// console.log(`result: ${util.inspect(result)}`)
|
// console.log(`result: ${util.inspect(result)}`)
|
||||||
|
|
||||||
assert.equal(res.statusCode, 400, 'Expect 400 status code')
|
assert.equal(res.statusCode, 422, 'Expect 422 status code')
|
||||||
|
|
||||||
assert.property(result, 'error')
|
assert.property(result, 'error')
|
||||||
assert.include(result.error, 'Unsupported address format')
|
assert.include(result.error, 'Unsupported address format')
|
||||||
@@ -1675,7 +1675,7 @@ describe('#Electrumx', () => {
|
|||||||
const result = await electrumxRoute.getMempool(req, res)
|
const result = await electrumxRoute.getMempool(req, res)
|
||||||
// console.log(`result: ${util.inspect(result)}`)
|
// console.log(`result: ${util.inspect(result)}`)
|
||||||
|
|
||||||
assert.equal(res.statusCode, 400, 'Expect 400 status code')
|
assert.equal(res.statusCode, 422, 'Expect 422 status code')
|
||||||
|
|
||||||
assert.property(result, 'error')
|
assert.property(result, 'error')
|
||||||
assert.include(result.error, 'Unsupported address format')
|
assert.include(result.error, 'Unsupported address format')
|
||||||
|
|||||||
@@ -110,8 +110,8 @@ describe('#slp', () => {
|
|||||||
req.body.tokenUtxos = [
|
req.body.tokenUtxos = [
|
||||||
{
|
{
|
||||||
tokenId:
|
tokenId:
|
||||||
'0a321bff9761f28e06a268b14711274bb77617410a16807bd0437ef234a072b1',
|
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
|
||||||
decimals: 0,
|
decimals: 8,
|
||||||
tokenQty: 2
|
tokenQty: 2
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
+3
-3
@@ -1287,7 +1287,7 @@ describe('#SLP', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('generateSendOpReturn()', () => {
|
describe('#generateSendOpReturn()', () => {
|
||||||
const generateSendOpReturn = slpRoute.generateSendOpReturn
|
const generateSendOpReturn = slpRoute.generateSendOpReturn
|
||||||
// Validate tokenUtxos input
|
// Validate tokenUtxos input
|
||||||
it('should throw 400 if tokenUtxos is missing', async () => {
|
it('should throw 400 if tokenUtxos is missing', async () => {
|
||||||
@@ -1363,8 +1363,8 @@ describe('#SLP', () => {
|
|||||||
req.body.tokenUtxos = [
|
req.body.tokenUtxos = [
|
||||||
{
|
{
|
||||||
tokenId:
|
tokenId:
|
||||||
'0a321bff9761f28e06a268b14711274bb77617410a16807bd0437ef234a072b1',
|
'38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
|
||||||
decimals: 0,
|
decimals: 8,
|
||||||
tokenQty: 2
|
tokenQty: 2
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user