diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f730cda8..9f04d6ab8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,8 @@ process and allows parallel rescans. - `lastCompaction` - when was the last compaction run. - Introduce `scan interactive` hook (start, filter) - Add `get median time` hook to get median time past for a blockhash. + - Add `get entries` hook to get entries. Similar to `get hashes`, but returns + encoded entries. ### hs-client Node - Introduce `scanInteractive` method that starts interactive rescan. @@ -40,6 +42,7 @@ process and allows parallel rescans. that returns scanAction object. - expects ws hook for `block rescan interactive abort` param `message`. - Adds `getMedianTime(blockhash)` that returns median time past of the block. + - Adds `getEntries(start, end)` that returns encoded chain entries. ### Wallet Changes #### Configuration diff --git a/lib/blockchain/chain.js b/lib/blockchain/chain.js index 85f05dacc..1a01d5478 100644 --- a/lib/blockchain/chain.js +++ b/lib/blockchain/chain.js @@ -2954,13 +2954,24 @@ class Chain extends AsyncEmitter { * Get range of hashes. * @param {Number} [start=-1] * @param {Number} [end=-1] - * @returns {Promise} + * @returns {Promise} */ getHashes(start = -1, end = -1) { return this.db.getHashes(start, end); } + /** + * Get range of entries. + * @param {Number} [start=-1] + * @param {Number} [end=-1] + * @returns {Promise} + */ + + getEntries(start = -1, end = -1) { + return this.db.getEntries(start, end); + } + /** * Get a coin (unspents only). * @private diff --git a/lib/blockchain/chaindb.js b/lib/blockchain/chaindb.js index 7f075349b..2f1c33a50 100644 --- a/lib/blockchain/chaindb.js +++ b/lib/blockchain/chaindb.js @@ -1111,7 +1111,7 @@ class ChainDB { * Get hash range. * @param {Number} [start=-1] * @param {Number} [end=-1] - * @returns {Promise} + * @returns {Promise} */ async getHashes(start = -1, end = -1) { @@ -1131,16 +1131,27 @@ class ChainDB { } /** - * Get all entries. - * @returns {Promise} - Returns {@link ChainEntry}[]. + * Get entries range. + * @param {Number} [start=-1] + * @param {Number} [end=-1] + * @returns {Promise} */ - async getEntries() { - return this.db.values({ - gte: layout.e.min(), - lte: layout.e.max(), - parse: data => ChainEntry.decode(data) - }); + async getEntries(start = -1, end = -1) { + if (start === -1) + start = 0; + + if (end === -1) + end >>>= 0; + + assert((start >>> 0) === start); + assert((end >>> 0) === end); + + const hashes = await this.getHashes(start, end); + + return Promise.all(hashes.map((hash) => { + return this.getEntryByHash(hash); + })); } /** diff --git a/lib/client/node.js b/lib/client/node.js index 5a8a3992c..75cc804fb 100644 --- a/lib/client/node.js +++ b/lib/client/node.js @@ -246,13 +246,24 @@ class NodeClient extends Client { * Get hashes. * @param {Number} [start=-1] * @param {Number} [end=-1] - * @returns {Promise} + * @returns {Promise} */ getHashes(start, end) { return this.call('get hashes', start, end); } + /** + * Get entries. + * @param {Number} [start=-1] + * @param {Number} [end=-1] + * @returns {Promise} - {@link ChainEntry} + */ + + getEntries(start, end) { + return this.call('get entries', start, end); + } + /** * Send a transaction. Do not wait for promise. * @param {TX} tx diff --git a/lib/node/http.js b/lib/node/http.js index 41aa5bc66..20d8295bb 100644 --- a/lib/node/http.js +++ b/lib/node/http.js @@ -633,6 +633,15 @@ class HTTP extends Server { return this.chain.getHashes(start, end); }); + socket.hook('get entries', async (...args) => { + const valid = new Validator(args); + const start = valid.i32(0, -1); + const end = valid.i32(1, -1); + + const entries = await this.chain.getEntries(start, end); + return entries.map(entry => entry.encode()); + }); + socket.hook('add filter', (...args) => { const valid = new Validator(args); const chunks = valid.array(0); diff --git a/test/node-http-test.js b/test/node-http-test.js index f6e4f5084..a5dfdefc7 100644 --- a/test/node-http-test.js +++ b/test/node-http-test.js @@ -8,6 +8,7 @@ const Witness = require('../lib/script/witness'); const Script = require('../lib/script/script'); const HDPrivateKey = require('../lib/hd/private'); const Output = require('../lib/primitives/output'); +const Block = require('../lib/primitives/block'); const Coin = require('../lib/primitives/coin'); const MTX = require('../lib/primitives/mtx'); const rules = require('../lib/covenants/rules'); @@ -430,10 +431,10 @@ describe('Node HTTP', function() { }); }); - describe('Websockets', function () { + describe('Websockets', function() { this.timeout(15000); - describe('Get entry and mtp', () => { + describe('Get entry and mtp', function() { const nodeCtx = new NodeContext({ wallet: true }); @@ -526,6 +527,52 @@ describe('Node HTTP', function() { }); }); + describe('get hashes and entries', function() { + const nodeCtx = new NodeContext({ + wallet: true + }); + + const {nclient, network} = nodeCtx; + const genesisBlock = Block.decode(network.genesisBlock); + let minedHashes; + + before(async () => { + await nodeCtx.open(); + + const {address} = await nodeCtx.wclient.createAddress('primary', 'default'); + minedHashes = await mineBlocks(nodeCtx, 15, address); + }); + + after(async () => { + await nodeCtx.close(); + }); + + it('should get hashes by range', async () => { + const hashes = await nclient.getHashes(0, 15); + assert(hashes && hashes.length === 16); + + for (const [index, hash] of hashes.entries()) { + if (index === 0) { + assert.bufferEqual(hash, genesisBlock.hash()); + continue; + } + + assert.bufferEqual(hash, minedHashes[index - 1]); + } + }); + + it('should get entries by range', async () => { + const entries = await nclient.getEntries(0, 15); + assert(entries && entries.length === 16); + + for (const rawEntry of entries) { + const entry = ChainEntry.decode(rawEntry); + const gotEntry = await nclient.getEntry(entry.hash); + assert.bufferEqual(rawEntry, gotEntry); + } + }); + }); + describe('tree commit', () => { const {types} = rules; @@ -655,6 +702,8 @@ async function mineBlocks(nodeCtx, count, address) { 'block connect', count ); - await nodeCtx.mineBlocks(count, address); + + const hashes = await nodeCtx.mineBlocks(count, address); await blockEvents; + return hashes; }