discard recently spent inputs from utxo selection
This commit is contained in:
parent
cf7d1cd55f
commit
3db28a4e2d
|
@ -902,50 +902,79 @@ WalletService.prototype._getUtxosForCurrentWallet = function(addresses, cb) {
|
|||
return utxo.txid + '|' + utxo.vout
|
||||
};
|
||||
|
||||
async.waterfall([
|
||||
var allAddresses, allUtxos, utxoIndex;
|
||||
|
||||
async.series([
|
||||
|
||||
function(next) {
|
||||
if (_.isArray(addresses)) {
|
||||
if (!_.isEmpty(addresses)) {
|
||||
next(null, addresses);
|
||||
} else {
|
||||
next(null, []);
|
||||
}
|
||||
} else {
|
||||
self.storage.fetchAddresses(self.walletId, next);
|
||||
allAddresses = addresses;
|
||||
return next();
|
||||
}
|
||||
self.storage.fetchAddresses(self.walletId, function(err, addresses) {
|
||||
allAddresses = addresses;
|
||||
return next();
|
||||
});
|
||||
},
|
||||
function(addresses, next) {
|
||||
if (addresses.length == 0) return next(null, []);
|
||||
function(next) {
|
||||
if (allAddresses.length == 0) return cb(null, []);
|
||||
|
||||
var addressStrs = _.pluck(addresses, 'address');
|
||||
var addressStrs = _.pluck(allAddresses, 'address');
|
||||
self._getUtxos(addressStrs, function(err, utxos) {
|
||||
if (err) return next(err);
|
||||
if (utxos.length == 0) return next(null, []);
|
||||
|
||||
allUtxos = utxos;
|
||||
utxoIndex = _.indexBy(allUtxos, utxoKey);
|
||||
return next();
|
||||
});
|
||||
},
|
||||
function(next) {
|
||||
self.getPendingTxs({}, function(err, txps) {
|
||||
if (err) return next(err);
|
||||
|
||||
var lockedInputs = _.map(_.flatten(_.pluck(txps, 'inputs')), utxoKey);
|
||||
var utxoIndex = _.indexBy(utxos, utxoKey);
|
||||
_.each(lockedInputs, function(input) {
|
||||
if (utxoIndex[input]) {
|
||||
utxoIndex[input].locked = true;
|
||||
}
|
||||
});
|
||||
|
||||
return next();
|
||||
});
|
||||
},
|
||||
function(next) {
|
||||
var fromTs = Math.floor(Date.now() / 1000) - 24 * 3600;
|
||||
self.storage.fetchTxs(self.walletId, {
|
||||
minTs: fromTs,
|
||||
limit: 100
|
||||
}, function(err, txs) {
|
||||
if (err) return next(err);
|
||||
var broadcasted = _.filter(txs, {
|
||||
status: 'broadcasted'
|
||||
});
|
||||
var spentInputs = _.map(_.flatten(_.pluck(broadcasted, 'inputs')), utxoKey);
|
||||
_.each(spentInputs, function(input) {
|
||||
if (utxoIndex[input]) {
|
||||
utxoIndex[input].spent = true;
|
||||
}
|
||||
});
|
||||
allUtxos = _.reject(allUtxos, {
|
||||
spent: true
|
||||
});
|
||||
return next();
|
||||
});
|
||||
},
|
||||
function(next) {
|
||||
// Needed for the clients to sign UTXOs
|
||||
var addressToPath = _.indexBy(addresses, 'address');
|
||||
_.each(utxos, function(utxo) {
|
||||
var addressToPath = _.indexBy(allAddresses, 'address');
|
||||
_.each(allUtxos, function(utxo) {
|
||||
utxo.path = addressToPath[utxo.address].path;
|
||||
utxo.publicKeys = addressToPath[utxo.address].publicKeys;
|
||||
});
|
||||
|
||||
return next(null, utxos);
|
||||
});
|
||||
});
|
||||
return next();
|
||||
},
|
||||
], cb);
|
||||
], function(err) {
|
||||
return cb(err, allUtxos);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1883,7 +1912,6 @@ WalletService.prototype.createTx = function(opts, cb) {
|
|||
var self = this;
|
||||
|
||||
self._runLocked(cb, function(cb) {
|
||||
|
||||
var wallet, txp, changeAddress;
|
||||
async.series([
|
||||
|
||||
|
@ -2442,14 +2470,14 @@ WalletService.prototype.getPendingTxs = function(opts, cb) {
|
|||
txp.deleteLockTime = self.getRemainingDeleteLockTime(txp);
|
||||
});
|
||||
|
||||
async.each(txps, function(txp, a_cb) {
|
||||
if (txp.status != 'accepted') return a_cb();
|
||||
async.each(txps, function(txp, next) {
|
||||
if (txp.status != 'accepted') return next();
|
||||
|
||||
self._checkTxInBlockchain(txp, function(err, isInBlockchain) {
|
||||
if (err || !isInBlockchain) return a_cb(err);
|
||||
if (err || !isInBlockchain) return next(err);
|
||||
self._processBroadcast(txp, {
|
||||
byThirdParty: true
|
||||
}, a_cb);
|
||||
}, next);
|
||||
});
|
||||
}, function(err) {
|
||||
return cb(err, _.reject(txps, function(txp) {
|
||||
|
|
|
@ -3469,7 +3469,7 @@ describe('Wallet service', function() {
|
|||
var server, wallet;
|
||||
beforeEach(function(done) {
|
||||
// log.level = 'debug';
|
||||
helpers.createAndJoinWallet(2, 3, function(s, w) {
|
||||
helpers.createAndJoinWallet(1, 2, function(s, w) {
|
||||
server = s;
|
||||
wallet = w;
|
||||
done();
|
||||
|
@ -3644,7 +3644,7 @@ describe('Wallet service', function() {
|
|||
var _old1 = Defaults.UTXO_SELECTION_MIN_TX_AMOUNT_VS_UTXO_FACTOR;
|
||||
var _old2 = Defaults.MAX_TX_SIZE_IN_KB;
|
||||
Defaults.UTXO_SELECTION_MIN_TX_AMOUNT_VS_UTXO_FACTOR = 0.0001;
|
||||
Defaults.MAX_TX_SIZE_IN_KB = 3;
|
||||
Defaults.MAX_TX_SIZE_IN_KB = 2;
|
||||
|
||||
helpers.stubUtxos(server, wallet, [100].concat(_.range(1, 20, 0)), function() {
|
||||
var txOpts = {
|
||||
|
@ -3826,7 +3826,7 @@ describe('Wallet service', function() {
|
|||
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
|
||||
amount: 200e2,
|
||||
}],
|
||||
feePerKb: 50e2,
|
||||
feePerKb: 80e2,
|
||||
};
|
||||
server.createTx(txOpts, function(err, txp) {
|
||||
should.exist(err);
|
||||
|
@ -3851,6 +3851,43 @@ describe('Wallet service', function() {
|
|||
});
|
||||
});
|
||||
});
|
||||
it('should not use UTXOs of recently broadcasted txs', function(done) {
|
||||
helpers.stubUtxos(server, wallet, [1, 1], function() {
|
||||
var txOpts = {
|
||||
outputs: [{
|
||||
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
|
||||
amount: 1.5e8,
|
||||
}],
|
||||
feePerKb: 100e2,
|
||||
};
|
||||
helpers.createAndPublishTx(server, txOpts, TestData.copayers[0].privKey_1H_0, function(txp) {
|
||||
should.exist(txp);
|
||||
var signatures = helpers.clientSign(txp, TestData.copayers[0].xPrivKey_44H_0H_0H);
|
||||
server.signTx({
|
||||
txProposalId: txp.id,
|
||||
signatures: signatures,
|
||||
}, function(err, txp) {
|
||||
should.not.exist(err);
|
||||
should.exist(txp);
|
||||
|
||||
helpers.stubBroadcast();
|
||||
server.broadcastTx({
|
||||
txProposalId: txp.id
|
||||
}, function(err, txp) {
|
||||
should.not.exist(err);
|
||||
should.exist(txp.txid);
|
||||
txp.status.should.equal('broadcasted');
|
||||
server.createTx(txOpts, function(err, txp) {
|
||||
should.exist(err);
|
||||
err.code.should.equal('INSUFFICIENT_FUNDS');
|
||||
should.not.exist(txp);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue