From cbde3233df8a634d976db3c8c29c21a93f1662ae Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Mon, 7 Mar 2016 15:18:32 -0300 Subject: [PATCH] handle lots of small inputs causing the total balance after fees to decrease --- lib/server.js | 28 +++++++++++++++++----------- test/integration/server.js | 21 +++++++++++++++++++-- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/lib/server.js b/lib/server.js index be9f80a..7acdd0f 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1273,6 +1273,12 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) { return cb(self._checkTxAndEstimateFee(txp)); } + var txpAmount = txp.getTotalAmount(); + var baseTxpSize = txp.getEstimatedSize(); + var baseTxpFee = baseTxpSize * txp.feePerKb / 1000.; + var sizePerInput = txp.getEstimatedSizeForSingleInput(); + var feePerInput = sizePerInput * txp.feePerKb / 1000.; + function excludeUtxos(utxos) { var excludeIndex = _.reduce(utxosToExclude, function(res, val) { res[val] = val; @@ -1284,6 +1290,15 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) { }); }; + function sanitizeUtxos(utxos) { + return _.filter(utxos, function(utxo) { + if (utxo.locked) return false; + if (utxo.satoshis <= feePerInput) return false; + if (txp.excludeUnconfirmedUtxos && !utxo.confirmations) return false; + return true; + }); + }; + function partitionUtxos(utxos) { return _.groupBy(utxos, function(utxo) { if (utxo.confirmations == 0) return '0' @@ -1293,14 +1308,9 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) { }; function select(utxos, cb) { - var txpAmount = txp.getTotalAmount(); - var baseTxpSize = txp.getEstimatedSize(); - var baseTxpFee = baseTxpSize * txp.feePerKb / 1000.; - var sizePerInput = txp.getEstimatedSizeForSingleInput(); - var feePerInput = sizePerInput * txp.feePerKb / 1000.; - var totalValueInUtxos = _.sum(utxos, 'satoshis'); var netValueInUtxos = totalValueInUtxos - baseTxpFee - (utxos.length * feePerInput); + if (totalValueInUtxos < txpAmount) { log.debug('Total value in all utxos (' + Utils.formatAmountInBtc(totalValueInUtxos) + ') is insufficient to cover for txp amount (' + Utils.formatAmountInBtc(txpAmount) + ')'); return cb(Errors.INSUFFICIENT_FUNDS); @@ -1422,11 +1432,7 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) { if (totalAmount < txp.getTotalAmount()) return cb(Errors.INSUFFICIENT_FUNDS); if (availableAmount < txp.getTotalAmount()) return cb(Errors.LOCKED_FUNDS); - // Prepare UTXOs list - utxos = _.reject(utxos, 'locked'); - if (txp.excludeUnconfirmedUtxos) { - utxos = _.filter(utxos, 'confirmations'); - } + utxos = sanitizeUtxos(utxos); log.debug('Considering ' + utxos.length + ' utxos (' + Utils.formatUtxos(utxos) + ')'); diff --git a/test/integration/server.js b/test/integration/server.js index e12ddcb..1b78eed 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -3412,8 +3412,6 @@ describe('Wallet service', function() { feePerKb: 100e2, }; server.createTx(txOpts, function(err, txp) { - console.log('*** [server.js ln3417] err:', err); // TODO - should.not.exist(err); should.exist(txp); txp.inputs.length.should.equal(1); @@ -3422,6 +3420,25 @@ describe('Wallet service', function() { }); }); }); + it('should ignore utxos too small to pay for fee', function(done) { + helpers.stubUtxos(server, wallet, ['1c200bit', '200bit'].concat(_.times(20, function() { + return '1bit'; + })), function() { + var txOpts = { + outputs: [{ + toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', + amount: 200e2, + }], + feePerKb: 90e2, + }; + server.createTx(txOpts, function(err, txp) { + should.not.exist(err); + should.exist(txp); + txp.inputs.length.should.equal(2); + done(); + }); + }); + }); }); });