From a8d709caf6ac815d69c9e8fd447fb87b5108ace8 Mon Sep 17 00:00:00 2001 From: Chris Kleeschulte Date: Tue, 8 Aug 2017 14:45:13 -0400 Subject: [PATCH] Added cache miss function for input values. --- lib/services/p2p/index.js | 5 +- lib/services/transaction/index.js | 62 +++++++++++++++++++++++-- test/services/transaction/index.unit.js | 14 ++++++ 3 files changed, 73 insertions(+), 8 deletions(-) diff --git a/lib/services/p2p/index.js b/lib/services/p2p/index.js index 931246a3..7f43ba5e 100644 --- a/lib/services/p2p/index.js +++ b/lib/services/p2p/index.js @@ -202,8 +202,7 @@ P2P.prototype._hasPeers = function() { }; P2P.prototype._initCache = function() { - this._inv = LRU(2000); - this._cache = []; + this._inv = LRU(1000); }; P2P.prototype._initP2P = function() { @@ -332,7 +331,7 @@ P2P.prototype._setResourceFilter = function(filter, resource) { }; P2P.prototype._startBcoin = function() { - const network = ['livenet', 'live', 'main', 'mainnet'].indexOf(this.node.network) !== -1? 'main' : 'testnet' + const network = ['livenet', 'live', 'main', 'mainnet'].indexOf(this.node.network) !== -1? 'main' : 'testnet'; this._bcoin = new Bcoin({ network: network, prefix: this.node.datadir diff --git a/lib/services/transaction/index.js b/lib/services/transaction/index.js index 8b9f4829..33b1f8eb 100644 --- a/lib/services/transaction/index.js +++ b/lib/services/transaction/index.js @@ -8,6 +8,8 @@ var _ = require('lodash'); var LRU = require('lru-cache'); var Unit = require('bitcore-lib').Unit; var log = require('../../index').log; +var async = require('async'); +var assert = require('assert'); function TransactionService(options) { BaseService.call(this, options); @@ -16,7 +18,7 @@ function TransactionService(options) { this._block = this.node.services.block; this._p2p = this.node.services.p2p; this._timestamp = this.node.services.timestamp; - this._inputValuesCache = LRU(1E6); // this should speed up syncing + this._inputValuesCache = LRU(500000); // this should speed up syncing } inherits(TransactionService, BaseService); @@ -48,9 +50,11 @@ TransactionService.prototype.getTransaction = function(txid, options, callback) var self = this; + var _tx; var queryMempool = _.isUndefined(options.queryMempool) ? true : options.queryMempool; var key = self.encoding.encodeTransactionKey(txid); + self._db.get(key, function(err, tx) { if(err) { @@ -67,9 +71,8 @@ TransactionService.prototype.getTransaction = function(txid, options, callback) if (memTx) { memTx.confirmations = 0; - return callback(null, memTx); + _tx = memTx; } - return callback(); }); @@ -77,16 +80,61 @@ TransactionService.prototype.getTransaction = function(txid, options, callback) if (tx) { tx.confirmations = self._p2p.getBestHeight - tx.__height; - return callback(null, tx); + _tx = tx; } - return callback(); } + if (!_tx) { + return callback(); + } + + // TODO: this could cause crazy amounts of recursion if input values are missing from the entire chain of txs + self._addMissingInputValues(_tx, callback); + }); }; +TransactionService.prototype._addMissingInputValues = function(tx, callback) { + + // if we have cache misses from when we populated input values, + // then we must go and find them after the fact (lazy-load). + var self = this; + async.eachOfLimit(tx.inputs, 4, function(input, index, next) { + + var inputSatoshis = tx.__inputValues[index]; + + if (inputSatoshis >= 0) { + return next(); + } + + var outputIndex = input.prevout.index; + self.getTransaction(input.prevout.txid(), function(err, _tx) { + + if (err || !_tx) { + return next(err || new Error('tx not found for tx id: ' + input.prevout.txid())); + } + + var output = _tx.outputs[outputIndex]; + assert(output, 'Expected an output, but did not get one for tx: ' + _tx.txid() + ' outputIndex: ' + outputIndex); + tx.__inputValues[index] = Unit.fromBTC(output.value).toSatoshis(); + next(); + + }); + + + }, function(err) { + + if (err) { + return callback(err); + } + + callback(null, tx); + + }); +}; + TransactionService.prototype.sendTransaction = function(tx, callback) { this._p2p.sendTransaction(tx, callback); }; @@ -216,6 +264,10 @@ TransactionService.prototype._processTransaction = function(tx, opts) { // input values tx.__inputValues = this._getInputValues(tx); //if there are any nulls here, this is a cache miss + if (_.compact(tx.__inputValues).length < tx.inputs.length) { + log.debug('Transaction Service: Cache miss for tx id: ' + tx.txid()); + } + //timestamp tx.__timestamp = this._getBlockTimestamp(opts.block.rhash()); //height diff --git a/test/services/transaction/index.unit.js b/test/services/transaction/index.unit.js index ad459a64..3f69d00e 100644 --- a/test/services/transaction/index.unit.js +++ b/test/services/transaction/index.unit.js @@ -122,4 +122,18 @@ describe('Transaction Service', function() { }); }); + describe('#_addMissingInputValues', function() { + it('should add missing input values on a tx', function(done) { + sandbox.stub(txService, 'getTransaction').callsArgWith(1, null, tx); + tx.__inputValues = []; + txService._addMissingInputValues(tx, function(err, tx) { + if (err) { + return done(err); + } + tx.__inputValues.should.deep.equal([113903300000000, 113903300000000, 50000000000000, 113903300000000 ]); + done(); + }); + + }); + }); });