bitcoind: get blocks and transactions as buffers

This commit is contained in:
Braydon Fuller 2016-04-08 15:49:18 -04:00
parent d11d0300de
commit b4b560aa45
2 changed files with 118 additions and 9 deletions

View File

@ -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) {

View File

@ -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);
}