diff --git a/lib/services/bitcoind.js b/lib/services/bitcoind.js index b45766f0..d6283cd5 100644 --- a/lib/services/bitcoind.js +++ b/lib/services/bitcoind.js @@ -63,9 +63,11 @@ Bitcoin.prototype._initCaches = function() { // caches valid indefinitely this.transactionCache = LRU(100000); + this.rawTransactionCache = LRU(50000); this.transactionInfoCache = LRU(100000); this.transactionInfoCacheConfirmations = 6; this.blockCache = LRU(144); + this.rawBlockCache = LRU(72); this.blockHeaderCache = LRU(288); this.zmqKnownTransactions = LRU(50); this.zmqLastBlock = 0; @@ -93,12 +95,14 @@ Bitcoin.prototype._initClients = function() { Bitcoin.prototype.getAPIMethods = function() { var methods = [ ['getBlock', this, this.getBlock, 1], + ['getRawBlock', this, this.getRawBlock, 1], ['getBlockHeader', this, this.getBlockHeader, 1], ['getBlockHashesByTimestamp', this, this.getBlockHashesByTimestamp, 2], ['getBestBlockHash', this, this.getBestBlockHash, 0], ['getInfo', this, this.getInfo, 0], ['syncPercentage', this, this.syncPercentage, 0], ['isSynced', this, this.isSynced, 0], + ['getRawTransaction', this, this.getRawTransaction, 1], ['getTransaction', this, this.getTransaction, 2], ['getTransactionWithBlockInfo', this, this.getTransactionWithBlockInfo, 2], ['sendTransaction', this, this.sendTransaction, 1], @@ -791,11 +795,9 @@ Bitcoin.prototype._getAddressDetailsForTransaction = function(transaction, addre */ Bitcoin.prototype._getDetailedTransaction = function(txid, options, next) { var self = this; - var queryMempool = _.isUndefined(options.queryMempool) ? true : options.queryMempool; self.getTransactionWithBlockInfo( txid, - queryMempool, function(err, transaction) { if (err) { return next(err); @@ -980,6 +982,46 @@ Bitcoin.prototype.getAddressSummary = function(addressArg, options, callback) { }; +/** + * Will retrieve a block as a Node.js Buffer + * @param {String|Number} block - A block hash or block height number + * @param {Function} callback + */ +Bitcoin.prototype.getRawBlock = function(blockArg, callback) { + // TODO apply performance patch to the RPC method for raw data + var self = this; + + function queryBlock(blockhash) { + self.client.getBlock(blockhash, false, function(err, response) { + if (err) { + return callback(self._wrapRPCError(err)); + } + var buffer = new Buffer(response.result, 'hex'); + self.rawBlockCache.set(blockhash, buffer); + callback(null, buffer); + }); + } + + var cachedBlock = self.rawBlockCache.get(blockArg); + if (cachedBlock) { + return setImmediate(function() { + callback(null, cachedBlock); + }); + } else { + if (_.isNumber(blockArg)) { + self.client.getBlockHash(blockArg, function(err, response) { + if (err) { + return callback(self._wrapRPCError(err)); + } + var blockhash = response.result; + queryBlock(blockhash); + }); + } else { + queryBlock(blockArg); + } + } +}; + /** * Will retrieve a block as a Bitcore object * @param {String|Number} block - A block hash or block height number @@ -995,7 +1037,7 @@ Bitcoin.prototype.getBlock = function(blockArg, callback) { return callback(self._wrapRPCError(err)); } var blockObj = bitcore.Block.fromString(response.result); - self.blockCache.set(blockArg, blockObj); + self.blockCache.set(blockhash, blockObj); callback(null, blockObj); }); } @@ -1122,13 +1164,39 @@ Bitcoin.prototype.sendTransaction = function(tx, allowAbsurdFees, callback) { }; +/** + * Will get a transaction as a Node.js Buffer. Results include the mempool. + * @param {String} txid - The transaction hash + * @param {Function} callback + */ +Bitcoin.prototype.getRawTransaction = function(txid, callback) { + var self = this; + var tx = self.rawTransactionCache.get(txid); + if (tx) { + return setImmediate(function() { + callback(null, tx); + }); + } else { + self.client.getRawTransaction(txid, function(err, response) { + if (err && response.error.code === -5) { + return callback(null, null); + } else if (err) { + return callback(self._wrapRPCError(err)); + } + var buffer = new Buffer(response.result, 'hex'); + self.rawTransactionCache.set(txid, buffer); + callback(null, buffer); + }); + } +}; + /** * Will get a transaction as a Bitcore Transaction. Results include the mempool. * @param {String} txid - The transaction hash * @param {Boolean} queryMempool - Include the mempool * @param {Function} callback */ -Bitcoin.prototype.getTransaction = function(txid, queryMempool, callback) { +Bitcoin.prototype.getTransaction = function(txid, callback) { var self = this; var tx = self.transactionCache.get(txid); if (tx) { @@ -1158,10 +1226,9 @@ Bitcoin.prototype.getTransaction = function(txid, queryMempool, callback) { * __timestamp: 1442951110, // in seconds * } * @param {String} txid - The transaction hash - * @param {Boolean} queryMempool - Include the mempool * @param {Function} callback */ -Bitcoin.prototype.getTransactionWithBlockInfo = function(txid, queryMempool, callback) { +Bitcoin.prototype.getTransactionWithBlockInfo = function(txid, callback) { var self = this; var tx = self.transactionInfoCache.get(txid); if (tx) { diff --git a/regtest/bitcoind.js b/regtest/bitcoind.js index 9b7a8f05..b9adf95e 100644 --- a/regtest/bitcoind.js +++ b/regtest/bitcoind.js @@ -149,6 +149,21 @@ describe('Bitcoind Functionality', function() { }); }); + describe('get blocks as buffers', function() { + [0,1,2,3,5,6,7,8,9].forEach(function(i) { + it('generated block ' + i, function(done) { + bitcoind.getRawBlock(blockHashes[i], function(err, block) { + if (err) { + throw err; + } + should.exist(block); + (block instanceof Buffer).should.equal(true); + done(); + }); + }); + }); + }); + describe('get errors as error instances', function() { it('will wrap an rpc into a javascript error', function(done) { bitcoind.client.getBlock(1000000000, function(err, response) { @@ -194,7 +209,7 @@ describe('Bitcoind Functionality', function() { var txhex = transactionData[i]; var tx = new bitcore.Transaction(); tx.fromString(txhex); - bitcoind.getTransaction(tx.hash, true, function(err, response) { + bitcoind.getTransaction(tx.hash, function(err, response) { if (err) { throw err; } @@ -206,7 +221,7 @@ describe('Bitcoind Functionality', function() { it('will return null if the transaction does not exist', function(done) { var txid = '6226c407d0e9705bdd7158e60983e37d0f5d23529086d6672b07d9238d5aa618'; - bitcoind.getTransaction(txid, true, function(err, response) { + bitcoind.getTransaction(txid, function(err, response) { if (err) { throw err; } @@ -214,7 +229,34 @@ describe('Bitcoind Functionality', function() { done(); }); }); + }); + describe('get transactions as buffers', function() { + [0,1,2,3,4,5,6,7,8,9].forEach(function(i) { + it('for tx ' + i, function(done) { + var txhex = transactionData[i]; + var tx = new bitcore.Transaction(); + tx.fromString(txhex); + bitcoind.getRawTransaction(tx.hash, function(err, response) { + if (err) { + throw err; + } + assert(response.toString('hex') === txhex, 'incorrect tx data result'); + done(); + }); + }); + }); + + it('will return null if the transaction does not exist', function(done) { + var txid = '6226c407d0e9705bdd7158e60983e37d0f5d23529086d6672b07d9238d5aa618'; + bitcoind.getRawTransaction(txid, function(err, response) { + if (err) { + throw err; + } + should.not.exist(response); + done(); + }); + }); }); describe('get block header', function() { @@ -390,7 +432,7 @@ describe('Bitcoind Functionality', function() { describe('get transaction with block info', function() { it('should include tx buffer, height and timestamp', function(done) { - bitcoind.getTransactionWithBlockInfo(utxos[0].txid, true, function(err, tx) { + bitcoind.getTransactionWithBlockInfo(utxos[0].txid, function(err, tx) { if (err) { return done(err); }