Merge pull request #305 from braydonf/bug/balance

Fixed bug with balance
This commit is contained in:
Patrick Nagurny 2015-10-12 13:50:56 -04:00
commit 93436429f1
2 changed files with 90 additions and 21 deletions

View File

@ -1048,8 +1048,12 @@ AddressService.prototype.getUnspentOutputsForAddress = function(address, queryMe
return callback(new errors.NoOutputs('Address ' + address + ' has no outputs'), []); return callback(new errors.NoOutputs('Address ' + address + ' has no outputs'), []);
} }
var opts = {
queryMempool: queryMempool
};
var isUnspent = function(output, callback) { var isUnspent = function(output, callback) {
self.isUnspent(output, callback); self.isUnspent(output, opts, callback);
}; };
async.filter(outputs, isUnspent, function(results) { async.filter(outputs, isUnspent, function(results) {
@ -1061,25 +1065,37 @@ AddressService.prototype.getUnspentOutputsForAddress = function(address, queryMe
/** /**
* Will give the inverse of isSpent * Will give the inverse of isSpent
* @param {Object} output * @param {Object} output
* @param {Object} options
* @param {Boolean} options.queryMempool - Include mempool in results
* @param {Function} callback * @param {Function} callback
*/ */
AddressService.prototype.isUnspent = function(output, callback) { AddressService.prototype.isUnspent = function(output, options, callback) {
this.isSpent(output, function(spent) { $.checkArgument(_.isFunction(callback));
this.isSpent(output, options, function(spent) {
callback(!spent); callback(!spent);
}); });
}; };
/** /**
* Will determine if an output is spent, results do not include the mempool. * Will determine if an output is spent.
* @param {Object} output - An output as returned from getOutputs * @param {Object} output - An output as returned from getOutputs
* @param {Object} options
* @param {Boolean} options.queryMempool - Include mempool in results
* @param {Function} callback * @param {Function} callback
*/ */
AddressService.prototype.isSpent = function(output, callback) { AddressService.prototype.isSpent = function(output, options, callback) {
$.checkArgument(_.isFunction(callback));
var queryMempool = _.isUndefined(options.queryMempool) ? true : options.queryMempool;
var self = this; var self = this;
var txid = output.prevTxId ? output.prevTxId.toString('hex') : output.txid; var txid = output.prevTxId ? output.prevTxId.toString('hex') : output.txid;
var spent = self.node.services.bitcoind.isSpent(txid, output.outputIndex);
if (!spent && queryMempool) {
var spentIndexKey = [txid, output.outputIndex].join('-');
spent = self.mempoolSpentIndex[spentIndexKey] ? true : false;
}
setImmediate(function() { setImmediate(function() {
callback(self.node.services.bitcoind.isSpent(txid, output.outputIndex)); // TODO error should be the first argument?
callback(spent);
}); });
}; };
@ -1179,7 +1195,7 @@ AddressService.prototype.getAddressSummary = function(address, options, callback
var txids = []; var txids = [];
for(var i = 0; i < outputs.length; i++) { for(var i = 0; i < outputs.length; i++) {
// Bitcoind's isSpent at the moment only works for confirmed transactions // Bitcoind's isSpent only works for confirmed transactions
var spentDB = self.node.services.bitcoind.isSpent(outputs[i].txid, outputs[i].outputIndex); var spentDB = self.node.services.bitcoind.isSpent(outputs[i].txid, outputs[i].outputIndex);
var spentIndexKey = [outputs[i].txid, outputs[i].outputIndex].join('-'); var spentIndexKey = [outputs[i].txid, outputs[i].outputIndex].join('-');
var spentMempool = self.mempoolSpentIndex[spentIndexKey]; var spentMempool = self.mempoolSpentIndex[spentIndexKey];

View File

@ -872,7 +872,7 @@ describe('Address Service', function() {
var am = new AddressService({node: mocknode}); var am = new AddressService({node: mocknode});
am.getOutputs = sinon.stub().callsArgWith(2, null, outputs); am.getOutputs = sinon.stub().callsArgWith(2, null, outputs);
am.isUnspent = function(output, callback) { am.isUnspent = function(output, options, callback) {
callback(!outputs[i].spent); callback(!outputs[i].spent);
i++; i++;
}; };
@ -914,24 +914,24 @@ describe('Address Service', function() {
}); });
it('should give true when isSpent() gives false', function(done) { it('should give true when isSpent() gives false', function(done) {
am.isSpent = sinon.stub().callsArgWith(1, false); am.isSpent = sinon.stub().callsArgWith(2, false);
am.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', function(unspent) { am.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', {}, function(unspent) {
unspent.should.equal(true); unspent.should.equal(true);
done(); done();
}); });
}); });
it('should give false when isSpent() gives true', function(done) { it('should give false when isSpent() gives true', function(done) {
am.isSpent = sinon.stub().callsArgWith(1, true); am.isSpent = sinon.stub().callsArgWith(2, true);
am.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', function(unspent) { am.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', {},function(unspent) {
unspent.should.equal(false); unspent.should.equal(false);
done(); done();
}); });
}); });
it('should give false when isSpent() returns an error', function(done) { it('should give false when isSpent() returns an error', function(done) {
am.isSpent = sinon.stub().callsArgWith(1, new Error('error')); am.isSpent = sinon.stub().callsArgWith(2, new Error('error'));
am.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', function(unspent) { am.isUnspent('1KiW1A4dx1oRgLHtDtBjcunUGkYtFgZ1W', {}, function(unspent) {
unspent.should.equal(false); unspent.should.equal(false);
done(); done();
}); });
@ -939,7 +939,6 @@ describe('Address Service', function() {
}); });
describe('#isSpent', function() { describe('#isSpent', function() {
var am;
var db = {}; var db = {};
var testnode = { var testnode = {
db: db, db: db,
@ -949,16 +948,70 @@ describe('Address Service', function() {
} }
} }
}; };
before(function() { it('should give true if bitcoind.isSpent gives true', function(done) {
am = new AddressService({node: testnode}); var am = new AddressService({node: testnode});
am.node.services.bitcoind = { am.node.services.bitcoind = {
isSpent: sinon.stub().returns(true), isSpent: sinon.stub().returns(true),
on: sinon.stub() on: sinon.stub()
}; };
am.isSpent({}, {}, function(spent) {
spent.should.equal(true);
done();
});
}); });
it('should give true if bitcoind.isSpent is false and mempoolSpentIndex is true', function(done) {
it('should give true if bitcoind.isSpent gives true', function(done) { var am = new AddressService({node: testnode});
am.isSpent('output', function(spent) { am.node.services.bitcoind = {
isSpent: sinon.stub().returns(false),
on: sinon.stub()
};
var txid = '3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7';
var outputIndex = 0;
var output = {
prevTxId: new Buffer(txid, 'hex'),
outputIndex: outputIndex
};
var spentKey = [txid, outputIndex].join('-');
am.mempoolSpentIndex[spentKey] = new Buffer(5);
am.isSpent(output, {queryMempool: true}, function(spent) {
spent.should.equal(true);
done();
});
});
it('should give false if spent in mempool with queryMempool set to false', function(done) {
var am = new AddressService({node: testnode});
am.node.services.bitcoind = {
isSpent: sinon.stub().returns(false),
on: sinon.stub()
};
var txid = '3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7';
var outputIndex = 0;
var output = {
prevTxId: new Buffer(txid, 'hex'),
outputIndex: outputIndex
};
var spentKey = [txid, outputIndex].join('-');
am.mempoolSpentIndex[spentKey] = new Buffer(5);
am.isSpent(output, {queryMempool: false}, function(spent) {
spent.should.equal(false);
done();
});
});
it('default to querying the mempool', function(done) {
var am = new AddressService({node: testnode});
am.node.services.bitcoind = {
isSpent: sinon.stub().returns(false),
on: sinon.stub()
};
var txid = '3b6bc2939d1a70ce04bc4f619ee32608fbff5e565c1f9b02e4eaa97959c59ae7';
var outputIndex = 0;
var output = {
prevTxId: new Buffer(txid, 'hex'),
outputIndex: outputIndex
};
var spentKey = [txid, outputIndex].join('-');
am.mempoolSpentIndex[spentKey] = new Buffer(5);
am.isSpent(output, {}, function(spent) {
spent.should.equal(true); spent.should.equal(true);
done(); done();
}); });