From fdd5255e59e76da30424baa616953ffbb2841a91 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Tue, 1 Mar 2016 17:40:07 -0300 Subject: [PATCH] add stop conditions for small utxos --- lib/server.js | 17 ++++++++++- test/integration/helpers.js | 4 +-- test/integration/server.js | 56 +++++++++++++++++++++++++++++++++---- 3 files changed, 69 insertions(+), 8 deletions(-) diff --git a/lib/server.js b/lib/server.js index 24b8342..88a1c41 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1349,6 +1349,8 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) { var UTXO_SELECTION_MAX_SINGLE_UTXO_FACTOR = 2; +var UTXO_SELECTION_MAX_FEE_FACTOR_FOR_SMALL_UTXOS = 2; +var UTXO_SELECTION_MAX_TX_AMOUNT_VS_FEE_FACTOR_FOR_SMALL_UTXOS = 0.002; WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) { var self = this; @@ -1386,6 +1388,7 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) { console.log('*** [server.js ln1362] ----------------------- select for amount of:', txpAmount); // TODO // TODO: fix for when fee is specified instead of feePerKb + var baseTxpFee = txp.getEstimatedSize() * txp.feePerKb / 1000.; var feePerInput = txp.getEstimatedSizeForSingleInput() * txp.feePerKb / 1000.; console.log('*** [server.js ln1375] feePerInput:', feePerInput); // TODO @@ -1407,7 +1410,19 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) { if (input.satoshis < feePerInput) return false; selected.push(input); - console.log('*** [server.js ln1380] input:', input.satoshis, ' aporta ->>> ', input.satoshis - feePerInput); // TODO + var txpFee = baseTxpFee + (feePerInput * selected.length); + + var amountVsFeeRatio = txpFee / txpAmount; + var singleInputFeeVsFeeRatio = txpFee / (baseTxpFee + feePerInput); + + console.log('*** [server.js ln1402] txpFee, amountVsFeeRatio, singleInputFeeVsFeeRatio:', txpFee, amountVsFeeRatio, singleInputFeeVsFeeRatio); // TODO + + + if (amountVsFeeRatio > UTXO_SELECTION_MAX_TX_AMOUNT_VS_FEE_FACTOR_FOR_SMALL_UTXOS && + (_.isEmpty(bigInputs) || (singleInputFeeVsFeeRatio > UTXO_SELECTION_MAX_FEE_FACTOR_FOR_SMALL_UTXOS))) + return false; + + console.log('*** [server.js ln1380] input:', input.satoshis, ' aporta:', input.satoshis - feePerInput); // TODO total += input.satoshis - feePerInput; if (total >= txpAmount) return false; diff --git a/test/integration/helpers.js b/test/integration/helpers.js index 1d43e8e..4a573b2 100644 --- a/test/integration/helpers.js +++ b/test/integration/helpers.js @@ -236,7 +236,7 @@ helpers._parseAmount = function(str) { result.amount = Utils.strip(match[2] * 1e8); break; case 'bit': - result.amount = Utils.strip(match[2] * 1e6); + result.amount = Utils.strip(match[2] * 1e2); break case 'sat': result.amount = Utils.strip(match[2]); @@ -268,7 +268,7 @@ helpers.stubUtxos = function(server, wallet, amounts, opts, cb) { var utxos = _.compact(_.map([].concat(amounts), function(amount, i) { var parsed = helpers._parseAmount(amount); - console.log('*** [helpers.js ln270] amount, result:', amount, parsed); // TODO + if (parsed.amount <= 0) return null; var address = addresses[i % addresses.length]; diff --git a/test/integration/server.js b/test/integration/server.js index f2a7007..94529f8 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -5741,10 +5741,28 @@ describe('Wallet service', function() { }); }); - it.skip('should select a single utxo if within thresholds relative to tx amount', function(done) {}); + it('should select a single utxo if within thresholds relative to tx amount', function(done) { + helpers.stubUtxos(server, wallet, [1, '350bit', '100bit', '100bit', '100bit'], function() { + var txOpts = { + outputs: [{ + toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', + amount: 20000, + }], + feePerKb: 1000, + }; + server.createTx(txOpts, function(err, txp) { + should.not.exist(err); + should.exist(txp); + txp.inputs.length.should.equal(1); + txp.inputs[0].satoshis.should.equal(35000); + + done(); + }); + }); + }); it('should select smaller utxos if within max fee constraints', function(done) { - helpers.stubUtxos(server, wallet, [1, 0.0001, 0.0001, 0.0001], function() { + helpers.stubUtxos(server, wallet, [1, '100bit', '100bit', '100bit'], function() { var txOpts = { outputs: [{ toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', @@ -5755,12 +5773,16 @@ describe('Wallet service', function() { server.createTx(txOpts, function(err, txp) { should.not.exist(err); should.exist(txp); + txp.inputs.length.should.equal(3); + _.all(txp.inputs, function(input) { + return input == 10000; + }); done(); }); }); }); it('should select smallest big utxo if small utxos are insufficient', function(done) { - helpers.stubUtxos(server, wallet, [3, 1, 2, 0.0001, 0.0001, 0.0001], function() { + helpers.stubUtxos(server, wallet, [3, 1, 2, '100bit', '100bit', '100bit'], function() { var txOpts = { outputs: [{ toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', @@ -5777,10 +5799,34 @@ describe('Wallet service', function() { }); }); }); - it.skip('should select smallest big utxo if small utxos exceed maximum fee', function(done) {}); + it.only('should select smallest big utxo if small utxos exceed maximum fee', function(done) { + helpers.stubUtxos(server, wallet, [3, 1, 2].concat(_.times(20, function() { + return '1000bit'; + })), function() { + var txOpts = { + outputs: [{ + toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', + amount: 12000e2, + }], + feePerKb: 20e2, + }; + server.createTx(txOpts, function(err, txp) { + console.log('*** [server.js ln5790] err:', err); // TODO + + should.not.exist(err); + should.exist(txp); + console.log('*** [server.js ln5792] txp.fee:', txp.fee); // TODO + + txp.inputs.length.should.equal(1); + txp.inputs[0].satoshis.should.equal(1e8); + + done(); + }); + }); + }); it.skip('should not fail with tx exceeded max size if there is at least 1 big input', function(done) {}); it('should ignore utxos not contributing enough to cover increase in fee', function(done) { - helpers.stubUtxos(server, wallet, [0.0001, 0.0001, 0.0001], function() { + helpers.stubUtxos(server, wallet, ['100bit', '100bit', '100bit'], function() { var txOpts = { outputs: [{ toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',