From 81e1ec13d2f69ca4788629c066c8df4751cf8179 Mon Sep 17 00:00:00 2001 From: Manuel Araoz Date: Wed, 11 Mar 2015 19:20:38 -0300 Subject: [PATCH] add /transactions/:hash/inputs|outputs/:index --- api/controllers/transactions.js | 40 ++++++++++++++++++++++++ api/routes/v1.js | 12 ++++--- api/test/v1/transactions.js | 55 ++++++++++++++++++++++++++++----- 3 files changed, 95 insertions(+), 12 deletions(-) diff --git a/api/controllers/transactions.js b/api/controllers/transactions.js index 78d61013..97807227 100644 --- a/api/controllers/transactions.js +++ b/api/controllers/transactions.js @@ -35,6 +35,15 @@ Transactions.txHashParam = function(req, res, next, txHash) { }); }; +/* + * sets an input or output index + */ +Transactions.indexParam = function(req, res, next, index) { + index = parseInt(index); + req.index = index; + next(); +}; + /* * controllers @@ -87,6 +96,32 @@ Transactions.list = function(req, res) { }); }; + +var buildIOHelper = function(name) { + $.checkArgument(name === 'inputs' || name === 'outputs'); + return function(req, res) { + $.checkState(req.tx instanceof Transaction); + if (_.isNumber(req.index)) { + res.send(req.tx[name][req.index].toJSON()); + return; + } + res.send(req.tx[name].map(function(x) { + return x.toJSON(); + })); + }; + +}; + +/** + * Returns a transaction's outputs + */ +Transactions.getInputs = buildIOHelper('inputs'); + +/** + * Returns a transaction's outputs + */ +Transactions.getOutputs = buildIOHelper('outputs'); + /** * errors */ @@ -102,4 +137,9 @@ Transactions.getTxError = function(req, res) { res.send('/v1/transactions/ parameter must be a 64 digit hex'); }; +Transactions.indexError = function(req, res) { + res.status(422); + res.send('index parameter must be a positive integer'); +}; + module.exports = Transactions; diff --git a/api/routes/v1.js b/api/routes/v1.js index ecad3b89..a60b858b 100644 --- a/api/routes/v1.js +++ b/api/routes/v1.js @@ -6,7 +6,6 @@ var Blocks = require('../controllers/blocks'); var Transactions = require('../controllers/transactions'); var Addresses = require('../controllers/addresses'); - function initRouter(node) { var router = express.Router(); @@ -25,6 +24,7 @@ function initRouter(node) { router.param('height', Blocks.heightParam); router.param('txHash', Transactions.txHashParam); router.param('address', Addresses.addressParam); + router.param('index', Transactions.indexParam); // Node routes router.get('/node', NodeStatus.getStatus); @@ -40,12 +40,12 @@ function initRouter(node) { router.post('/transactions/send', Transactions.send); // Input routes - router.get('/transactions/:txHash([A-Fa-f0-9]{64})/inputs', mockResponse); - router.get('/transactions/:txHash([A-Fa-f0-9]{64})/inputs/:index([0-9]+)', mockResponse); + router.get('/transactions/:txHash([A-Fa-f0-9]{64})/inputs', Transactions.getInputs); + router.get('/transactions/:txHash([A-Fa-f0-9]{64})/inputs/:index([0-9]+)', Transactions.getInputs); // Output routes - router.get('/transactions/:txHash([A-Fa-f0-9]{64})/outputs', mockResponse); - router.get('/transactions/:txHash([A-Fa-f0-9]{64})/outputs/:index([0-9]+)', mockResponse); + router.get('/transactions/:txHash([A-Fa-f0-9]{64})/outputs', Transactions.getOutputs); + router.get('/transactions/:txHash([A-Fa-f0-9]{64})/outputs/:index([0-9]+)', Transactions.getOutputs); // Address routes router.get('/addresses/:address', Addresses.get); @@ -56,6 +56,8 @@ function initRouter(node) { // error routes router.get('/blocks/*', Blocks.getBlockError); + router.get('/transactions/:txHash([A-Fa-f0-9]{64})/inputs/*', Transactions.indexError); + router.get('/transactions/:txHash([A-Fa-f0-9]{64})/outputs/*', Transactions.indexError); router.get('/transactions/*', Transactions.getTxError); return router; diff --git a/api/test/v1/transactions.js b/api/test/v1/transactions.js index 9a8d1272..4da665dc 100644 --- a/api/test/v1/transactions.js +++ b/api/test/v1/transactions.js @@ -40,16 +40,23 @@ describe('BitcoreHTTP v1 transactions routes', function() { agent = request(app); }); + var failsWithInvalidHash = function(agent, url, cb) { + agent.get(url) + .expect(422) + .expect('/v1/transactions/ parameter must be a 64 digit hex', cb); + }; + var reportsNotFound = function(agent, url, cb) { + agent.get(url) + .expect(404) + .expect('Transaction with id 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b600000000 not found', cb); + }; + describe('/transactions/:txHash', function() { it('fails with invalid txHash', function(cb) { - agent.get('/v1/transactions/abad1dea') - .expect(422) - .expect('/v1/transactions/ parameter must be a 64 digit hex', cb); + failsWithInvalidHash(agent, '/v1/transactions/abad1dea', cb); }); it('returns 404 with non existent transaction', function(cb) { - agent.get('/v1/transactions/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b600000000') - .expect(404) - .expect('Transaction with id 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b600000000 not found', cb); + reportsNotFound(agent, '/v1/transactions/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b600000000', cb); }); Object.keys(mockTransactions).forEach(function(hash) { it('works with valid txHash ...' + hash.substring(hash.length - 8), function(cb) { @@ -99,5 +106,39 @@ describe('BitcoreHTTP v1 transactions routes', function() { .expect('Unable to broadcast transaction 8c14f0db3df150123e6f3dbbf30f8b955a8249b62ac1d1ff16284aefa3d06d87', cb); }); }); - + var testIO = function(name) { + describe('/transactions/:txHash/' + name + '/', function() { + it('fails with invalid txHash', function(cb) { + failsWithInvalidHash(agent, '/v1/transactions/abad1dea/' + name, cb); + }); + it('returns 404 with non existent transaction', function(cb) { + reportsNotFound(agent, + '/v1/transactions/000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b600000000/' + name, cb); + }); + Object.keys(mockTransactions).forEach(function(hash) { + var tx = mockTransactions[hash]; + var summary = hash.substring(hash.length - 8); + it('works with valid txHash ...' + summary + 'getting all ' + name, function(cb) { + agent.get('/v1/transactions/' + hash + '/' + name + '/') + .expect(200) + .expect(tx[name].map(function(x) { + return x.toJSON(); + }), cb); + }); + var canGetSpecificInput = function(i) { + var x = tx[name][i]; + return function(cb) { + agent.get('/v1/transactions/' + hash + '/' + name + '/' + i) + .expect(200) + .expect(x.toJSON(), cb); + }; + }; + for (var i = 0; i < tx[name].length; i++) { + it('works with valid txHash ...' + summary + ' ' + name + ' ' + i, canGetSpecificInput(i)); + } + }); + }); + }; + testIO('inputs'); + testIO('outputs'); });