From 4c8f11ed34ab03f021d8710ffd821ae71070fc31 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 15 Apr 2019 21:25:12 -0700 Subject: [PATCH] node: update http for addrindex --- lib/node/fullnode.js | 14 +++++--- lib/node/http.js | 16 ++++++++- test/indexer-test.js | 81 ++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 100 insertions(+), 11 deletions(-) diff --git a/lib/node/fullnode.js b/lib/node/fullnode.js index 73e973f8..72b96b96 100644 --- a/lib/node/fullnode.js +++ b/lib/node/fullnode.js @@ -153,7 +153,8 @@ class FullNode extends Node { port: this.config.uint('http-port'), apiKey: this.config.str('api-key'), noAuth: this.config.bool('no-auth'), - cors: this.config.bool('cors') + cors: this.config.bool('cors'), + maxTxs: this.config.uint('max-txs') }); // Indexers @@ -175,7 +176,8 @@ class FullNode extends Node { blocks: this.blocks, chain: this.chain, memory: this.config.bool('memory'), - prefix: this.config.filter('index').str('prefix') || this.config.prefix + prefix: this.config.filter('index').str('prefix') || this.config.prefix, + maxTxs: this.config.uint('max-txs') }); } @@ -461,14 +463,18 @@ class FullNode extends Node { * Retrieve transactions pertaining to an * address from the mempool or chain database. * @param {Address} addr + * @param {Object} options + * @param {Number} options.limit + * @param {Number} options.reverse + * @param {Buffer} options.after * @returns {Promise} - Returns {@link TXMeta}[]. */ - async getMetaByAddress(addr) { + async getMetaByAddress(addr, options = {}) { const mempool = this.mempool.getMetaByAddress(addr); if (this.txindex && this.addrindex) { - const hashes = await this.addrindex.getHashesByAddress(addr); + const hashes = await this.addrindex.getHashesByAddress(addr, options); const mtxs = []; for (const hash of hashes) { diff --git a/lib/node/http.js b/lib/node/http.js index 40e80f32..2edd8cb2 100644 --- a/lib/node/http.js +++ b/lib/node/http.js @@ -193,12 +193,20 @@ class HTTP extends Server { this.get('/tx/address/:address', async (req, res) => { const valid = Validator.fromRequest(req); const address = valid.str('address'); + const limit = valid.uint('limit', this.options.maxTxs); + const reverse = valid.bool('reverse', false); + const after = valid.brhash('after', null); enforce(address, 'Address is required.'); enforce(!this.chain.options.spv, 'Cannot get TX in SPV mode.'); + enforce(limit <= this.options.maxTxs, + `Limit above max of ${this.options.maxTxs}.`); const addr = Address.fromString(address, this.network); - const metas = await this.node.getMetaByAddress(addr); + + const metas = await this.node.getMetaByAddress( + addr, {limit, reverse, after}); + const result = []; for (const meta of metas) { @@ -635,6 +643,7 @@ class HTTPOptions { this.apiHash = sha256.digest(Buffer.from(this.apiKey, 'ascii')); this.noAuth = false; this.cors = false; + this.maxTxs = 100; this.prefix = null; this.host = '127.0.0.1'; @@ -721,6 +730,11 @@ class HTTPOptions { this.certFile = options.certFile; } + if (options.maxTxs != null) { + assert(Number.isSafeInteger(options.maxTxs)); + this.maxTxs = options.maxTxs; + } + // Allow no-auth implicitly // if we're listening locally. if (!options.apiKey) { diff --git a/test/indexer-test.js b/test/indexer-test.js index 86b9cb9f..cd45499c 100644 --- a/test/indexer-test.js +++ b/test/indexer-test.js @@ -208,8 +208,16 @@ describe('Indexer', function() { const vectors = [ // Secret for the vectors: // cVDJUtDjdaM25yNVVDLLX3hcHUfth4c7tY3rSc4hy9e8ibtCuj6G - {addr: 'bcrt1qngw83fg8dz0k749cg7k3emc7v98wy0c7azaa6h', amount: 19.99}, - {addr: 'muZpTpBYhxmRFuCjLc7C6BBDF32C8XVJUi', amount: 1.99} + { + addr: 'bcrt1qngw83fg8dz0k749cg7k3emc7v98wy0c7azaa6h', + amount: 19.99, + label: 'p2wpkh' + }, + { + addr: 'muZpTpBYhxmRFuCjLc7C6BBDF32C8XVJUi', + amount: 1.99, + label: 'p2pkh' + } ]; const txids = []; @@ -295,8 +303,8 @@ describe('Indexer', function() { await node.close(); }); - it('will get txs by address', async () => { - for (const v of vectors) { + for (const v of vectors) { + it(`will get txs by ${v.label} address`, async () => { const res = await nclient.request( 'GET', `/tx/address/${v.addr}`, {}); @@ -304,7 +312,68 @@ describe('Indexer', function() { for (const tx of res) assert(txids.includes(tx.hash)); - } - }); + }); + + it(`will get txs by ${v.label} address (limit)`, async () => { + const res = await nclient.request( + 'GET', `/tx/address/${v.addr}`, {limit: 3}); + + for (const tx of res) + assert(txids.includes(tx.hash)); + }); + + it(`txs by ${v.label} address (reverse)`, async () => { + const asc = await nclient.request( + 'GET', `/tx/address/${v.addr}`, {reverse: false}); + + const dsc = await nclient.request( + 'GET', `/tx/address/${v.addr}`, {reverse: true}); + + for (let i = 0; i < dsc.length; i++) + assert.equal(asc[i].hash, dsc[dsc.length - i - 1].hash); + }); + + it(`txs by ${v.label} address after txid`, async () => { + const one = await nclient.request( + 'GET', `/tx/address/${v.addr}`, {limit: 3}); + assert.strictEqual(one.length, 3); + + const hash = one[2].hash; + + const two = await nclient.request( + 'GET', `/tx/address/${v.addr}`, {after: hash, limit: 3}); + assert.strictEqual(one.length, 3); + + const all = await nclient.request( + 'GET', `/tx/address/${v.addr}`, {limit: 6}); + assert.strictEqual(one.length, 3); + + assert.deepEqual(one.concat(two), all); + }); + + it(`txs by ${v.label} address after txid (reverse)`, async () => { + const one = await nclient.request( + 'GET', `/tx/address/${v.addr}`, + {limit: 3, reverse: true}); + + assert.strictEqual(one.length, 3); + + const hash = one[2].hash; + + const two = await nclient.request( + 'GET', `/tx/address/${v.addr}`, + {after: hash, limit: 3, reverse: true}); + + assert.strictEqual(one.length, 3); + + const all = await nclient.request( + 'GET', `/tx/address/${v.addr}`, + {limit: 6, reverse: true}); + + assert.strictEqual(one.length, 3); + + assert.deepEqual(one.concat(two), all); + }); + } }); });