diff --git a/package-lock.json b/package-lock.json index 4677ee3..849bf37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5686,9 +5686,9 @@ } }, "node_modules/tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.6.tgz", + "integrity": "sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g==", "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -10615,9 +10615,9 @@ } }, "tar": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz", - "integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==", + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.6.tgz", + "integrity": "sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g==", "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", diff --git a/src/bitcoin-node-connection.js b/src/bitcoin-node-connection.js index e0da16a..346a603 100644 --- a/src/bitcoin-node-connection.js +++ b/src/bitcoin-node-connection.js @@ -15,10 +15,12 @@ class BitcoinNodeConnection { constructor (zmq, rpc) { this.zmq = zmq this.rpc = rpc + this.isRestApiEnabled = false } async connect (_height, _network) { await this.zmq.connect() + this.isRestApiEnabled = await this.rpc.isRestApiEnabled() } async disconnect () { @@ -49,15 +51,21 @@ class BitcoinNodeConnection { return { reorg: true } } - if (blockData.size >= 0xf000000) { // Avoids create a string longer than the limit + if (this.isRestApiEnabled) { + const block = this._parseBlock( + await this.rpc.getRawBlockByHash(blockData.hash), + targetBlockHeight + ) + return this._buildBlockResponse(block, targetBlockHeight) + } else if (blockData.size >= 0xf000000) { return this._responsefromBlockData(blockData) + } else { + const block = this._parseBlock( + await this.rpc.getBlockByHeight(targetBlockHeight, false), + targetBlockHeight + ) + return this._buildBlockResponse(block, targetBlockHeight) } - - const block = this._parseBlock( - await this.rpc.getBlockByHeight(targetBlockHeight, false), - targetBlockHeight - ) - return this._buildBlockResponse(block, targetBlockHeight) } async listenForMempool (mempoolTxCallback) { @@ -90,8 +98,8 @@ class BitcoinNodeConnection { return response } - _parseBlock (rpcResponse, requestedHeight) { - const bsvBlock = new bsv.Block(Buffer.from(rpcResponse, 'hex')) + _parseBlock (blockBuffer, requestedHeight) { + const bsvBlock = new bsv.Block(blockBuffer) return { height: requestedHeight, diff --git a/src/bitcoin-rpc.js b/src/bitcoin-rpc.js index 2ad2eeb..9d20f66 100644 --- a/src/bitcoin-rpc.js +++ b/src/bitcoin-rpc.js @@ -28,6 +28,11 @@ class BitcoinRpc { this.baseUrl = baseUrl } + async isRestApiEnabled () { + const response = fetch(`${this.baseUrl}/rest/tx/9834daa6d34690981888f7db4c1c36686ebb9b685d37115abc38e0e75f9cd98d.hex`) + return response.ok + } + /** * @param {String} txid */ @@ -47,6 +52,11 @@ class BitcoinRpc { return this._rpcCall('getblockbyheight', [targetHeight, verbose]) } + async getRawBlockByHash (targetHash) { + const response = await fetch(`${this.baseUrl}/rest/block/${targetHash}.bin`) + return Buffer.from(await response.arrayBuffer()) + } + async _rpcCall (method, params) { const response = await this._httpPost(this.baseUrl, { jsonrpc: '1.0', diff --git a/src/indexer.js b/src/indexer.js index a334690..7e57fad 100644 --- a/src/indexer.js +++ b/src/indexer.js @@ -117,7 +117,7 @@ class Indexer { } _onAddTransaction (txid) { - this.logger.info('Added', txid) + this.logger.debug('Added', txid) } _onDeleteTransaction (txid) { diff --git a/test/bitcoin-node-connection-test.js b/test/bitcoin-node-connection-test.js index a7fa731..8afa2ce 100644 --- a/test/bitcoin-node-connection-test.js +++ b/test/bitcoin-node-connection-test.js @@ -19,6 +19,11 @@ class TestBitcoinRpc { } ] this.nextBlockHeight = 1001 + this.isRestEnabled = true + } + + async isRestApiEnabled () { + return this.isRestEnabled } async getRawTransaction (txid, verbose = true) { @@ -41,11 +46,20 @@ class TestBitcoinRpc { return { size: block.size || block.hex.length, previousblockhash: block.previousblockhash, - tx: block.txs.map(tx => tx.hash) + tx: block.txs.map(tx => tx.hash), + hash: block.hash } } } + async getRawBlockByHash (targetHash) { + if (!this.isRestEnabled) { + throw new Error('rest api is not enabled') + } + const block = this.blocks.find(block => block.hash === targetHash) + return Buffer.from(block.hex, 'hex') + } + // Test registerConfirmedTx (txid, rawTx) { @@ -106,6 +120,10 @@ class TestZmq { this.handler = handler } + async connect () { + // nothing + } + // test publishTx (tx) { @@ -173,10 +191,11 @@ describe('BitcoinNodeConnection', () => { let instance = null let run = null - beforeEach(() => { + beforeEach(async () => { bitcoinZmq = new TestZmq() bitcoinRpc = new TestBitcoinRpc() instance = new BitcoinNodeConnection(bitcoinZmq, bitcoinRpc) + await instance.connect() run = new Run({ purse: { @@ -373,6 +392,41 @@ describe('BitcoinNodeConnection', () => { const nextBlock = await instance.getNextBlock(previousBlock.height, null) expect(nextBlock.txids).to.eql([randomRunTx.hash]) }) + + describe('when rest-api is not enabled and a giant block arrives', () => { + beforeEach(() => { + bitcoinRpc.isRestEnabled = false + }) + it('does not get txs one by one', async () => { + const randomTx = buildRandomTx() + const randomRunTx = await buildRandomRunTx(run) + bitcoinRpc.registerUnconfirmedTx(randomTx.hash, randomTx.toBuffer().toString('hex')) + bitcoinRpc.registerUnconfirmedTx(randomRunTx.hash, randomRunTx.toBuffer().toString('hex')) + bitcoinRpc.closeBlock(0x1fffffe8 + 1) + const previousBlock = bitcoinRpc.blocks[bitcoinRpc.blocks.length - 2] + + expect(instance.getNextBlock(previousBlock.height, null)).not.to.eventually.throw() + }) + }) + + describe('when rest-api is enabled and a giant block arrives', () => { + beforeEach(() => { + bitcoinRpc.isRestEnabled = true + }) + it('does not call getRawTransaction', async () => { + const randomTx = buildRandomTx() + const randomRunTx = await buildRandomRunTx(run) + bitcoinRpc.registerUnconfirmedTx(randomTx.hash, randomTx.toBuffer().toString('hex')) + bitcoinRpc.registerUnconfirmedTx(randomRunTx.hash, randomRunTx.toBuffer().toString('hex')) + bitcoinRpc.closeBlock(0x1fffffe8 + 1) + + bitcoinRpc.getRawTransaction = () => { throw new Error('should not call') } + const previousBlock = bitcoinRpc.blocks[bitcoinRpc.blocks.length - 2] + + const nextBlock = await instance.getNextBlock(previousBlock.height, null) + expect(nextBlock.txhexs.length).to.eql(nextBlock.txids.length) + }) + }) }) describe('#listenForMempool', () => {