diff --git a/src/middleware/route-ratelimit.js b/src/middleware/route-ratelimit.js index 1df3284..08e3623 100644 --- a/src/middleware/route-ratelimit.js +++ b/src/middleware/route-ratelimit.js @@ -61,6 +61,10 @@ class RateLimits { redisClient.disconnect() } + async wipeRedis () { + await redisClient.flushdb() + } + // CT 2/7/20: Older rate-limiting code that does not scale well. /* This function controls the tierd request-per-minute (RPM) rate limits. diff --git a/src/routes/v3/full-node/rawtransactions.js b/src/routes/v3/full-node/rawtransactions.js index 515c989..0459c5f 100644 --- a/src/routes/v3/full-node/rawtransactions.js +++ b/src/routes/v3/full-node/rawtransactions.js @@ -395,6 +395,13 @@ class RawTransactions { return res.json({ error: 'txid can not be empty' }) } + if (txid.length !== 64) { + res.status(400) + return res.json({ + error: `parameter 1 must be of length 64 (not ${txid.length})` + }) + } + const data = await _this.getRawTransactionsFromNode(txid, verbose) return res.json(data) diff --git a/src/routes/v3/slp.js b/src/routes/v3/slp.js index 97cb386..6bea4d7 100644 --- a/src/routes/v3/slp.js +++ b/src/routes/v3/slp.js @@ -27,8 +27,9 @@ util.inspect.defaultOptions = { depth: 5 } // https://gist.github.com/christroutner/fc717ca704dec3dded8b52fae387eab2 const SLPDB_PASS = process.env.SLPDB_PASS ? process.env.SLPDB_PASS : 'BITBOX' -// const transactions = require("./insight/transaction") -const rawtransactions = require('./full-node/rawtransactions') +// const rawtransactions = require('./full-node/rawtransactions') +const RawTransactions = require('./full-node/rawtransactions') +const rawTransactions = new RawTransactions() // Setup REST and TREST URLs used by slpjs // Dev note: this allows for unit tests to mock the URL. @@ -1381,7 +1382,7 @@ async function txDetails (req, res, next) { // Get information on the transaction from Insight API. // const retData = await transactions.transactionsFromInsight(txid) - const retData = await rawtransactions.getRawTransactionsFromNode(txid, true) + const retData = await rawTransactions.getRawTransactionsFromNode(txid, true) // console.log(`retData: ${JSON.stringify(retData, null, 2)}`) // Return both the tx data from Insight and the formatted token information. diff --git a/test/v3/rate-limits.js b/test/v3/rate-limits.js index 01f8e37..54319e9 100644 --- a/test/v3/rate-limits.js +++ b/test/v3/rate-limits.js @@ -29,7 +29,7 @@ const jwt = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkYWRlM2Y1NzM5ZTZjMG describe('#route-ratelimits & jwt-auth', () => { let sandbox - before(() => { + before(async () => { // Save existing environment variables. // originalEnvVars = { // BITCOINCOM_BASEURL: process.env.BITCOINCOM_BASEURL, @@ -39,6 +39,10 @@ describe('#route-ratelimits & jwt-auth', () => { // } if (!process.env.JWT_AUTH_SERVER) { process.env.JWT_AUTH_SERVER = 'http://fakeurl.com/' } + + // Wipe the Redis DB, which prevents false negatives when running integration + // tests back-to-back. + await rateLimits.wipeRedis() }) // Setup the mocks before each test. diff --git a/test/v3/raw-transactions.js b/test/v3/raw-transactions.js index cd28992..7396f1a 100644 --- a/test/v3/raw-transactions.js +++ b/test/v3/raw-transactions.js @@ -133,7 +133,7 @@ describe('#Raw-Transactions', () => { process.env.RPC_BASEURL = 'http://fakeurl/api/' req.params.hex = - '0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000' + '0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000' // Mock the timeout error. sandbox.stub(uut.axios, 'request').throws({ code: 'ECONNABORTED' }) @@ -159,7 +159,7 @@ describe('#Raw-Transactions', () => { process.env.RPC_BASEURL = 'http://fakeurl/api/' req.params.hex = - '0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000' + '0200000001b9b598d7d6d72fc486b2b3a3c03c79b5bade6ec9a77ced850515ab5e64edcc21010000006b483045022100a7b1b08956abb8d6f322aa709d8583c8ea492ba0585f1a6f4f9983520af74a5a0220411aee4a9a54effab617b0508c504c31681b15f9b187179b4874257badd4139041210360cfc66fdacb650bc4c83b4e351805181ee696b7d5ab4667c57b2786f51c413dffffffff0210270000000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac786e9800000000001976a914eb4b180def88e3f5625b2d8ae2c098ff7d85f66488ac00000000' // Mock the timeout error. sandbox.stub(uut.axios, 'request').throws({ code: 'ECONNREFUSED' }) @@ -630,12 +630,12 @@ describe('#Raw-Transactions', () => { req.body.txids = ['abc123'] const result = await uut.getRawTransactionBulk(req, res) - // console.log(`result: ${util.inspect(result)}`) assert.hasAllKeys(result, ['error']) assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') assert.include(result.error, 'parameter 1 must be of length 64 (not 6)') }) + it('returns proper error when downstream service stalls', async () => { // Save the existing RPC URL. const savedUrl2 = process.env.RPC_BASEURL @@ -765,9 +765,10 @@ describe('#Raw-Transactions', () => { const result = await uut.getRawTransactionSingle(req, res) // console.log(`result: ${util.inspect(result)}`) + // console.log(`res.statusCode: ${res.statusCode}`) assert.hasAllKeys(result, ['error']) - assert.equal(res.statusCode, 500, 'HTTP status code 400 expected.') + assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') assert.include(result.error, 'parameter 1 must be of length 64 (not 6)') }) it('returns proper error when downstream service stalls', async () => { @@ -904,12 +905,14 @@ describe('#Raw-Transactions', () => { assert.include(result.error, 'Encountered empty hex') }) - it('should throw 500 error if hex is invalid', async () => { + it('should throw 400 error if hex is invalid', async () => { // Mock the RPC call for unit tests. if (process.env.TEST === 'unit') { - sandbox - .stub(uut.axios, 'request') - .rejects('TX decode failed') + sandbox.stub(uut.axios, 'request').rejects({ + response: { + data: { error: { code: -22, message: 'TX decode failed' } } + } + }) } req.body.hexes = ['abc123'] @@ -918,9 +921,10 @@ describe('#Raw-Transactions', () => { // console.log(`result: ${util.inspect(result)}`) assert.hasAllKeys(result, ['error']) - assert.equal(res.statusCode, 500, 'HTTP status code 400 expected.') + assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') assert.include(result.error, 'TX decode failed') }) + it('returns proper error when downstream service stalls', async () => { // Save the existing RPC URL. const savedUrl2 = process.env.RPC_BASEURL @@ -978,9 +982,12 @@ describe('#Raw-Transactions', () => { // Mock the RPC call for unit tests. if (process.env.TEST === 'unit') { - sandbox - .stub(uut.axios, 'request') - .resolves({ data: { result: 'aef8848396e67532b42008b9d75b5a5a3459a6717740f31f0553b74102b4b118' } }) + sandbox.stub(uut.axios, 'request').resolves({ + data: { + result: + 'aef8848396e67532b42008b9d75b5a5a3459a6717740f31f0553b74102b4b118' + } + }) } req.body.hexes = [ @@ -1059,18 +1066,21 @@ describe('#Raw-Transactions', () => { // Mock the RPC call for unit tests. if (process.env.TEST === 'unit') { - sandbox - .stub(uut.axios, 'request') - .rejects('TX decode failed') + sandbox.stub(uut.axios, 'request').rejects({ + response: { + data: { error: { code: -22, message: 'TX decode failed' } } + } + }) } const result = await uut.sendRawTransactionSingle(req, res) // console.log(`result: ${util.inspect(result)}`) assert.hasAllKeys(result, ['error']) - assert.equal(res.statusCode, 500, 'HTTP status code 400 expected.') + assert.equal(res.statusCode, 400, 'HTTP status code 400 expected.') assert.include(result.error, 'TX decode failed') }) + it('returns proper error when downstream service stalls', async () => { // Save the existing RPC URL. const savedUrl2 = process.env.RPC_BASEURL @@ -1121,6 +1131,7 @@ describe('#Raw-Transactions', () => { // Restore the saved URL. process.env.RPC_BASEURL = savedUrl2 }) + it('should GET /sendRawTransaction/:hex', async () => { // This is a difficult test to run as transaction hex is invalid after a // block confirmation. So the unit tests simulates what the output 'should' @@ -1128,9 +1139,12 @@ describe('#Raw-Transactions', () => { // Mock the RPC call for unit tests. if (process.env.TEST === 'unit') { - sandbox - .stub(uut.axios, 'request') - .resolves({ data: { result: 'aef8848396e67532b42008b9d75b5a5a3459a6717740f31f0553b74102b4b118' } }) + sandbox.stub(uut.axios, 'request').resolves({ + data: { + result: + 'aef8848396e67532b42008b9d75b5a5a3459a6717740f31f0553b74102b4b118' + } + }) } req.params.hex =