diff --git a/lib/server.js b/lib/server.js index 718e276..dc6e2e4 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1375,13 +1375,28 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) { }); }; + function formatAmount(amount) { + return Utils.formatAmount(amount, 'btc') + 'btc'; + }; + + function formatInputs(inputs) { + if (_.isEmpty(inputs)) return 'none'; + return _.map([].concat(inputs), function(i) { + var amount = formatAmount(i.satoshis); + var confirmations = i.confirmations ? i.confirmations + 'c' : 'u'; + return amount + '/' + confirmations; + }).join(', '); + }; + + function formatRatio(ratio) { + return (ratio * 100.).toFixed(4) + '%'; + }; + + function formatSize(size) { + return (size / 1000.).toFixed(4) + 'kB'; + }; + function select(utxos) { - var i = 0; - var total = 0; - var selected = []; - - console.log('*** [server.js ln1362] ----------------------- select for amount of:', txpAmount); // TODO - // TODO: fix for when fee is specified instead of feePerKb var txpAmount = txp.getTotalAmount(); var baseTxpSize = txp.getEstimatedSize(); @@ -1391,13 +1406,15 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) { var totalValueInUtxos = _.sum(utxos, 'satoshis') - baseTxpFee - (utxos.length * feePerInput); if (totalValueInUtxos < txpAmount) { - console.log('*** [server.js ln1380] value in all utxos:', totalValueInUtxos, ' is inusfficient to cover for txp amount:', txpAmount); // TODO - + log.debug('Value in all utxos (' + formatAmount(totalValueInUtxos) + ') is inusufficient to cover for txp amount (' + formatAmount(txpAmount) + ')'); // TODO return false; } + var bigInputThreshold = txpAmount * Defaults.UTXO_SELECTION_MAX_SINGLE_UTXO_FACTOR; + log.debug('Big input threshold ' + formatAmount(bigInputThreshold)); + var partitions = _.partition(utxos, function(utxo) { - return utxo.satoshis > txpAmount * Defaults.UTXO_SELECTION_MAX_SINGLE_UTXO_FACTOR; + return utxo.satoshis > bigInputThreshold; }); var bigInputs = _.sortBy(partitions[0], 'satoshis'); @@ -1405,17 +1422,22 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) { return -utxo.satoshis; }); - console.log('*** [server.js ln1386] bigInputs:', _.pluck(bigInputs, 'satoshis')); // TODO - console.log('*** [server.js ln1386] smallInputs:', _.pluck(smallInputs, 'satoshis')); // TODO + log.debug('Considering ' + bigInputs.length + ' big inputs (' + formatInputs(bigInputs) + ')'); + log.debug('Considering ' + smallInputs.length + ' small inputs (' + formatInputs(smallInputs) + ')'); + var total = 0; + var selected = []; _.each(smallInputs, function(input, i) { - console.log('****************************** [server.js ln1395] INPUT #', i); // TODO + log.debug('Input #' + i + ': ' + formatInputs(input)); - if (input.satoshis < feePerInput) return false; + if (input.satoshis < feePerInput) { + log.debug('The input does not cover the extra fees (' + formatAmount(feePerInput) + ')'); + return false; + } var inputAmount = input.satoshis - feePerInput; - console.log('*** [server.js ln1398] inputAmount:', inputAmount); // TODO + log.debug('The input contributes ' + formatAmount(inputAmount)); selected.push(input); @@ -1426,42 +1448,41 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) { var singleInputFeeVsFeeRatio = txpFee / (baseTxpFee + feePerInput); var amountVsUtxoRatio = inputAmount / txpAmount; - console.log('*** [server.js ln1402] txpSize, txpFee', txpSize, txpFee); // TODO - console.log('*** [server.js ln1403] amountVsFeeRatio, singleInputFeeVsFeeRatio, amountVsUtxoRatio:', amountVsFeeRatio, singleInputFeeVsFeeRatio, amountVsUtxoRatio); // TODO + log.debug('Tx amount/Fee: ' + formatRatio(amountVsFeeRatio) + ' (max: ' + formatRatio(Defaults.UTXO_SELECTION_MAX_TX_AMOUNT_VS_FEE_FACTOR) + ')'); + log.debug('Single-input fee/Multi-input fee: ' + formatRatio(singleInputFeeVsFeeRatio) + ' (max: ' + formatRatio(Defaults.UTXO_SELECTION_MAX_FEE_VS_SINGLE_UTXO_FEE_FACTOR) + ')' + ' loses wrt single-input tx: ' + formatAmount((selected.length - 1) * feePerInput)); + log.debug('Tx amount/input amount:' + formatRatio(amountVsUtxoRatio) + ' (min: ' + formatRatio(Defaults.UTXO_SELECTION_MIN_TX_AMOUNT_VS_UTXO_FACTOR) + ')'); if (!_.isEmpty(bigInputs)) { if ((amountVsFeeRatio > Defaults.UTXO_SELECTION_MAX_TX_AMOUNT_VS_FEE_FACTOR && singleInputFeeVsFeeRatio > Defaults.UTXO_SELECTION_MAX_FEE_VS_SINGLE_UTXO_FEE_FACTOR)) { - console.log('*** [server.js ln1413] breaking because fee exceeded fee/amount ratio and fee is too expensive compared to single input tx'); // TODO + log.debug('Breaking because fee exceeded fee/amount ratio and fee is too expensive compared to single input tx'); return false; } if (amountVsUtxoRatio < Defaults.UTXO_SELECTION_MIN_TX_AMOUNT_VS_UTXO_FACTOR) { - console.log('*** [server.js ln1418] breaking because utxo is too small compared to tx amount'); // TODO + log.debug('Breaking because utxo is too small compared to tx amount'); return false; } if (txpSize / 1000. > Defaults.MAX_TX_SIZE_IN_KB) { - console.log('*** [server.js ln1423] breaking because tx size is too big'); // TODO + log.debug('Breaking because tx size is too big (' + formatSize(txpSize) + ')'); // TODO return false; } } - console.log('*** [server.js ln1380] input:', input.satoshis, ' aporta:', input.satoshis - feePerInput); // TODO - total += inputAmount; - console.log('*** [server.js ln1421] total:', total); // TODO + log.debug('Cumuled total so far: ' + formatAmount(total)); if (total >= txpAmount) return false; }); if (total < txpAmount) { - console.log('*** [server.js ln1401] could not reach txp total, still missing:', txpAmount - total); // TODO + log.debug('Could not reach Txp total (' + formatAmount(txpAmount) + '), still missing: ' + formatAmount(txpAmount - total)); selected = []; if (!_.isEmpty(bigInputs)) { - console.log('*** [server.js ln1405] using big inputs!:', _.first(bigInputs).satoshis); // TODO + log.debug('Using big input: ', formatInputs(_.first(bigInputs))); var input = _.first(bigInputs); total = input.satoshis; selected = [input]; @@ -1469,12 +1490,17 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) { } if (!_.isEmpty(selected)) { - console.log('*** [server.js ln1400] SUCCESS! locked overhead:', total - txpAmount, ' ratio to txp:', (total - txpAmount) / txpAmount); // TODO + var lockedOverhead = total - txpAmount; + log.debug('SUCCESS! Total locked: ' + formatAmount(total) + ', overhead: ' + formatAmount(lockedOverhead) + ' (' + formatRatio(lockedOverhead / txpAmount) + ')'); + } else { + log.debug('Could not find enough funds within this utxso subset'); } return selected; }; + log.debug('Selecting inputs for a ' + formatAmount(txp.getTotalAmount()) + ' txp'); + self._getUtxosForCurrentWallet(null, function(err, utxos) { if (err) return cb(err); @@ -1501,6 +1527,8 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) { utxos = _.filter(utxos, 'confirmations'); } + log.debug('Considering ' + utxos.length + ' utxos (' + formatInputs(utxos) + ')'); + var inputs = []; var groups = [6, 1, 0]; var lastGroupLength; @@ -1509,17 +1537,21 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) { return utxo.confirmations >= group; }); - // If this group does not have any new elements, skip it - if (lastGroupLength === candidateUtxos.length) return; - lastGroupLength = candidateUtxos.length; + log.debug('Group >= ' + group); - console.log('*** [server.js ln1415] group >=', group, '\n', _.map(candidateUtxos, function(u) { - return _.pick(u, 'satoshis', 'confirmations') - })); // TODO + // If this group does not have any new elements, skip it + if (lastGroupLength === candidateUtxos.length) { + log.debug('This group is identical to the one already explored'); + return; + } + + log.debug('Candidate utxos: ' + formatInputs(candidateUtxos)); + + lastGroupLength = candidateUtxos.length; inputs = select(candidateUtxos); - console.log('*** [server.js ln1418] inputs:', _.pluck(inputs, 'satoshis')); // TODO + log.debug('Selected inputs from this group: ' + formatInputs(inputs)); if (!_.isEmpty(inputs)) return false; }); @@ -1532,30 +1564,6 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) { var bitcoreError = self._checkTxAndEstimateFee(txp); return cb(bitcoreError); - - // var i = 0; - // var total = 0; - // var selected = []; - - // var bitcoreTx, bitcoreError; - - // function select() { - // if (i >= inputs.length) return cb(bitcoreError || new Error('Could not select tx inputs')); - - // var input = inputs[i++]; - // selected.push(input); - // total += input.satoshis; - // if (total >= txp.getTotalAmount()) { - // txp.setInputs(selected); - // bitcoreError = self._checkTxAndEstimateFee(txp); - // if (!bitcoreError) return cb(); - // if (txp.getEstimatedSize() / 1000 > Defaults.MAX_TX_SIZE_IN_KB) - // return cb(Errors.TX_MAX_SIZE_EXCEEDED); - // } - // setTimeout(select, 0); - // }; - - // select(); }); };