diff --git a/api/controllers/addresses.js b/api/controllers/addresses.js new file mode 100644 index 00000000..1c0fc232 --- /dev/null +++ b/api/controllers/addresses.js @@ -0,0 +1,62 @@ +'use strict'; + +var bitcore = require('bitcore'); +var _ = bitcore.deps._; +var $ = bitcore.util.preconditions; +var Address = bitcore.Address; + +var BitcoreNode = require('../../'); + +var Addresses = {}; + +var node; +Addresses.setNode = function(aNode) { + node = aNode; +}; + + +/* + * params + */ + +/* + * Finds a block by its hash + */ +Addresses.blockHashParam = function(req, res, next, blockHash) { + node.getBlock(blockHash) + .then(function(block) { + req.block = block; + }) + .then(next) + .catch(BitcoreNode.errors.Addresses.NotFound, function() { + res.status(404).send('Block with id ' + blockHash + ' not found'); + }); +}; + +/* + * Finds an address' info by it's string representation + */ +Addresses.addressParam = function(req, res, next, address) { + if (!Address.isValid(address)) { + res.status(422); + res.send('/v1/addresses/ parameter must be a valid bitcoin address'); + return; + } + req.address = new Address(address); + next(); +}; + + +/* + * controllers + */ + +Addresses.get = function(req, res) { + $.checkState(req.address instanceof Address); + node.getAddressInfo(req.address) + .then(function(info) { + res.send(info); + }); +}; + +module.exports = Addresses; diff --git a/api/routes/v1.js b/api/routes/v1.js index c0c8dddf..37a74ad3 100644 --- a/api/routes/v1.js +++ b/api/routes/v1.js @@ -4,12 +4,13 @@ var express = require('express'); var NodeStatus = require('../controllers/node'); var Blocks = require('../controllers/blocks'); var Transactions = require('../controllers/transactions'); +var Addresses = require('../controllers/addresses'); function initRouter(node) { var router = express.Router(); - [NodeStatus, Blocks, Transactions].forEach(function(controller) { + [NodeStatus, Blocks, Transactions, Addresses].forEach(function(controller) { controller.setNode(node); }); @@ -23,6 +24,7 @@ function initRouter(node) { router.param('blockHash', Blocks.blockHashParam); router.param('height', Blocks.heightParam); router.param('txHash', Transactions.txHashParam); + router.param('address', Addresses.addressParam); // Node routes router.get('/node', NodeStatus.getStatus); @@ -46,7 +48,7 @@ function initRouter(node) { router.get('/transactions/:txHash([A-Fa-f0-9]{64})/outputs/:index([0-9]+)', mockResponse); // Address routes - router.get('/addresses/:address', mockResponse); + router.get('/addresses/:address', Addresses.get); router.get('/addresses/:address/transactions', mockResponse); router.get('/addresses/:address/utxos', mockResponse); // TODO: check if this is really restful diff --git a/api/test/data/addresses.js b/api/test/data/addresses.js new file mode 100644 index 00000000..eebe1541 --- /dev/null +++ b/api/test/data/addresses.js @@ -0,0 +1,68 @@ +'use strict'; + +var mockAddresses = { + '1CT9huFgxMFveRvzZ7zPPJNoaMm2Fo64VH': { + address: '1CT9huFgxMFveRvzZ7zPPJNoaMm2Fo64VH', + transactions: [ + 'b944ef8c77f9b5f4a4276880f17256988bba4d0125abc54391548061a688ae09' + ], + unconfirmed: { + balance: 5000000000, + received: 5000000000, + sent: 0, + }, + confirmed: { + balance: 5000000000, + received: 5000000000, + sent: 0, + } + }, + '1HZH6zHri1qc68s34MmE5MwG9xstbkFavo': { + address: '1HZH6zHri1qc68s34MmE5MwG9xstbkFavo', + transactions: [ + '07ebb557e5782d4b9b7180c5b0b299ab1249d28f3454ccc19d4e7bd819e5ec35', + '7b309cef1b87471baee38a533c850ce25350f10e88a64e04da1ee08a69dbbba1', + '0c88e745b5c1dffccc39a96f3e25e9486bcafde82b92441f463859df15685959', + ], + unconfirmed: { + balance: 200000043000, + received: 200000043000, + sent: 0, + }, + confirmed: { + balance: 200000043000, + received: 200000043000, + sent: 0, + } + + }, + '1CEXio2gSCozXeSuKQJCDMEpgHfaiT48A3': { + address: '1CEXio2gSCozXeSuKQJCDMEpgHfaiT48A3', + transactions: [ + '07ebb557e5782d4b9b7180c5b0b299ab1249d28f3454ccc19d4e7bd819e5ec35', + 'b6025e6835966b31f40a9f0bb4a1717df0976ec23934934d2b2580a884c09b68', + '6ae158f49c25435c472f1533bce7d090f9edeb75b20fc30297ee78c962f4295a', + '35dd6607d21b3b0739fc0696d0633eaaa26f5ab10e2cbb0fa12353c2ccff6f83', + 'f14c1e10e8b0657068df4d53d8d93d1eb6b1f699041f7d505d5c482479c59634', + '9aa72c5b116a12f80b2d38b1f7bb43356d3a4f02637e7ac5abfeebb14862a3f8', + '9a0a957583f5ea390b2b5573ace7d67a876aeb66c59ada5c0d79a6b7affb34f6', + '585d59d3223eef73ccdc3c19b4e85cb0cc66ea818f173cf6d54723785c7210a1', + '2952d4f79d2388c3cb931e92699ded43fe3b92f2a58f03ee0c68a0a5b0d73e46', + 'f4e18bfbd9edc5ac0cfdd5b0869d77ef5cd38908afe106c02d189ac835569c87', + '4fb1495d114e6853acbe95c38f0acad1b8f790f8979148015e8fbfc3d0c394e9', + ], + unconfirmed: { + balance: 93350245, + received: 1230747491, + sent: 1137397246, + }, + confirmed: { + balance: 93350245, + received: 1230747491, + sent: 1137397246, + } + + }, +}; + +module.exports = mockAddresses; diff --git a/api/test/v1/addresses.js b/api/test/v1/addresses.js new file mode 100644 index 00000000..49b605fd --- /dev/null +++ b/api/test/v1/addresses.js @@ -0,0 +1,45 @@ +'use strict'; + +var chai = require('chai'); +var should = chai.should(); +var request = require('supertest'); + +var EventEmitter = require('eventemitter2').EventEmitter2; +var Promise = require('bluebird'); +Promise.longStackTraces(); +var bitcore = require('bitcore'); +var _ = bitcore.deps._; + +var BitcoreHTTP = require('../../lib/http'); +var BitcoreNode = require('../../../'); +var mockAddresses = require('../data/addresses'); + +describe('BitcoreHTTP v1 addresses routes', function() { + + // mocks + var nodeMock, app, agent; + beforeEach(function() { + nodeMock = new EventEmitter(); + nodeMock.getAddressInfo = function(address) { + return Promise.resolve(mockAddresses[address.toString()]); + }; + app = new BitcoreHTTP(nodeMock).app; + agent = request(app); + }); + + describe('/addresses/:addresss', function() { + it('fails with invalid address', function(cb) { + agent.get('/v1/addresses/1BpbpfLdY7oBS9gK7aDXgvMgr1DpvNH3B2') + .expect(422) + .expect('/v1/addresses/ parameter must be a valid bitcoin address', cb); + }); + Object.keys(mockAddresses).forEach(function(addr) { + var info = mockAddresses[addr]; + it('works with valid address ' + addr, function(cb) { + agent.get('/v1/addresses/' + addr) + .expect(200) + .expect(JSON.stringify(info), cb); + }); + }); + }); +});