Compare commits

..
10 Commits
9 changed files with 3203 additions and 4633 deletions
+12 -1
View File
@@ -1,11 +1,22 @@
/* /*
Common configuration settings. Common configuration settings.
Default settings in this file can be overridden by an environment variable.
*/ */
const config = { const config = {
// This is the same secret used by jwt-bch-api
apiTokenSecret: process.env.TOKENSECRET apiTokenSecret: process.env.TOKENSECRET
? process.env.TOKENSECRET ? process.env.TOKENSECRET
: 'secret-jwt-token' : 'secret-jwt-token',
// Rate Limits
anonRateLimit: process.env.ANON_RATE_LIMIT ? process.env.ANON_RATE_LIMIT : 50,
whitelistRateLimit: process.env.WHITELIST_RATE_LIMIT
? process.env.WHITELIST_RATE_LIMIT
: 10,
whitelistDomains: process.env.WHITELIST_DOMAINS
? process.env.WHITELIST_DOMAINS.split(',')
: ['fullstack.cash', 'psfoundation.cash']
} }
module.exports = config module.exports = config
+3115 -4612
View File
File diff suppressed because it is too large Load Diff
+5 -4
View File
@@ -17,20 +17,21 @@
"test": "npm run lint && npm run test-v4", "test": "npm run lint && npm run test-v4",
"lint": "standard --env mocha --fix", "lint": "standard --env mocha --fix",
"test-v4": "export NETWORK=mainnet && nyc --reporter=text mocha --timeout 60000 test/v4/", "test-v4": "export NETWORK=mainnet && nyc --reporter=text mocha --timeout 60000 test/v4/",
"test:temp": "export NETWORK=mainnet && export TEST=integration && mocha --timeout 25000 test/v4/integration/price.js",
"test:integration": "mocha test/v4/integration", "test:integration": "mocha test/v4/integration",
"test:integration:slpdb": "mocha --timeout 25000 -g '#validate2Single' test/v4/integration/slp*.js", "test:integration:slpdb": "mocha --timeout 25000 -g '#validate2Single' test/v4/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/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 -g '#generateSendOpReturn()' test/v4/integration/",
"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.2",
"apidoc": "^0.23.0", "apidoc": "^0.23.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",
+2 -2
View File
@@ -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
} }
+37 -9
View File
@@ -12,12 +12,17 @@
'use strict' 'use strict'
// Public npm libraries.
const jwt = require('jsonwebtoken') const jwt = require('jsonwebtoken')
// local libraries.
const wlogger = require('../util/winston-logging') const wlogger = require('../util/winston-logging')
const config = require('../../config') const config = require('../../config')
const ANON_LIMITS = 50 const ANON_LIMITS = config.anonRateLimit
const WHITELIST_RATE_LIMIT = config.whitelistRateLimit
const WHITELIST_DOMAINS = config.whitelistDomains
const INTERNAL_RATE_LIMIT = 1
// Redis // Redis
const redisOptions = { const redisOptions = {
@@ -144,13 +149,11 @@ class RateLimits {
// If the request originates from one of the approved wallet apps, then // If the request originates from one of the approved wallet apps, then
// apply paid-access rate limits. // apply paid-access rate limits.
if ( // console.log(`origin: ${JSON.stringify(origin, null, 2)}`)
origin && // console.log(`whitelist: ${JSON.stringify(WHITELIST_DOMAINS, null, 2)}`)
(origin.toString().indexOf('fullstack.cash') > -1 || const isInWhitelist = _this.isInWhitelist(origin)
origin.toString().indexOf('splitbch.com') > -1 || if (isInWhitelist) {
origin.toString().indexOf('slp-api') > -1) pointsToConsume = WHITELIST_RATE_LIMIT
) {
pointsToConsume = 10
res.locals.pointsToConsume = pointsToConsume // Feedback for tests. res.locals.pointsToConsume = pointsToConsume // Feedback for tests.
} }
@@ -161,7 +164,7 @@ class RateLimits {
// Do not comment out this line. // Do not comment out this line.
key.toString().indexOf('172.17.') > -1 key.toString().indexOf('172.17.') > -1
) { ) {
pointsToConsume = 1 pointsToConsume = INTERNAL_RATE_LIMIT
res.locals.pointsToConsume = pointsToConsume // Feedback for tests. res.locals.pointsToConsume = pointsToConsume // Feedback for tests.
} }
@@ -263,6 +266,31 @@ class RateLimits {
throw err throw err
} }
} }
// Returns a boolean if the origin of the request matches a domain in the
// whitelist.
isInWhitelist (origin) {
try {
const retVal = false // Default value.
if (!origin) return false
// console.log(`WHITELIST_DOMAINS: ${JSON.stringify(WHITELIST_DOMAINS, null, 2)}`)
for (let i = 0; i < WHITELIST_DOMAINS.length; i++) {
const thisDomain = WHITELIST_DOMAINS[i]
if (origin.toString().indexOf(thisDomain) > -1) {
return true
}
}
return retVal
} catch (err) {
wlogger.error('Error in route-ratelimit.js/isInWhitelist(). Returning false by default.')
return false
}
}
} }
module.exports = RateLimits module.exports = RateLimits
+3
View File
@@ -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.
+2 -2
View File
@@ -110,8 +110,8 @@ describe('#slp', () => {
req.body.tokenUtxos = [ req.body.tokenUtxos = [
{ {
tokenId: tokenId:
'0a321bff9761f28e06a268b14711274bb77617410a16807bd0437ef234a072b1', '38e97c5d7d3585a2cbf3f9580c82ca33985f9cb0845d4dcce220cb709f9538b0',
decimals: 0, decimals: 8,
tokenQty: 2 tokenQty: 2
} }
] ]
+24
View File
@@ -445,6 +445,30 @@ describe('#route-ratelimits & jwt-auth', () => {
assert.equal(res.locals.pointsToConsume, 50) assert.equal(res.locals.pointsToConsume, 50)
}) })
}) })
describe('#isInWhitelist', () => {
it('should return false when no argument is passed in', () => {
const result = rateLimits.isInWhitelist()
assert.equal(result, false)
})
it('should return false when origin is not in the whitelist', () => {
const origin = 'blah.com'
const result = rateLimits.isInWhitelist(origin)
assert.equal(result, false)
})
it('should return true when origin is in the whitelist', () => {
const origin = 'message.fullstack.cash'
const result = rateLimits.isInWhitelist(origin)
assert.equal(result, true)
})
})
}) })
// Generates a Basic authorization header. // Generates a Basic authorization header.
+3 -3
View File
@@ -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
} }
] ]