Browse Source

improve error handling

activeAddress
Ivan Socolsky 9 years ago
parent
commit
2581f488fa
  1. 53
      lib/server.js
  2. 6
      test/integration/server.js

53
lib/server.js

@ -1243,6 +1243,9 @@ WalletService.prototype._checkTxAndEstimateFee = function(txp) {
txp.estimateFee(); txp.estimateFee();
if (txp.getEstimatedSize() / 1000 > Defaults.MAX_TX_SIZE_IN_KB)
return Errors.TX_MAX_SIZE_EXCEEDED;
try { try {
var bitcoreTx = txp.getBitcoreTx(); var bitcoreTx = txp.getBitcoreTx();
bitcoreError = bitcoreTx.getSerializationError(serializationOpts); bitcoreError = bitcoreTx.getSerializationError(serializationOpts);
@ -1337,8 +1340,6 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) {
txp.setInputs(selected); txp.setInputs(selected);
bitcoreError = self._checkTxAndEstimateFee(txp); bitcoreError = self._checkTxAndEstimateFee(txp);
if (!bitcoreError) return cb(); if (!bitcoreError) return cb();
if (txp.getEstimatedSize() / 1000 > Defaults.MAX_TX_SIZE_IN_KB)
return cb(Errors.TX_MAX_SIZE_EXCEEDED);
} }
setTimeout(select, 0); setTimeout(select, 0);
}; };
@ -1404,10 +1405,15 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) {
var sizePerInput = txp.getEstimatedSizeForSingleInput(); var sizePerInput = txp.getEstimatedSizeForSingleInput();
var feePerInput = sizePerInput * txp.feePerKb / 1000.; var feePerInput = sizePerInput * txp.feePerKb / 1000.;
var totalValueInUtxos = _.sum(utxos, 'satoshis') - baseTxpFee - (utxos.length * feePerInput); var totalValueInUtxos = _.sum(utxos, 'satoshis');
var netValueInUtxos = totalValueInUtxos - baseTxpFee - (utxos.length * feePerInput);
if (totalValueInUtxos < txpAmount) { if (totalValueInUtxos < txpAmount) {
log.debug('Value in all utxos (' + formatAmount(totalValueInUtxos) + ') is inusufficient to cover for txp amount (' + formatAmount(txpAmount) + ')'); // TODO log.debug('Total value in all utxos (' + formatAmount(totalValueInUtxos) + ') is insufficient to cover for txp amount (' + formatAmount(txpAmount) + ')'); // TODO
return false; return Errors.INSUFFICIENT_FUNDS;
}
if (netValueInUtxos < txpAmount) {
log.debug('Value after fees in all utxos (' + formatAmount(netValueInUtxos) + ') is insufficient to cover for txp amount (' + formatAmount(txpAmount) + ')'); // TODO
return Errors.INSUFFICIENT_FUNDS_FOR_FEE;
} }
var bigInputThreshold = txpAmount * Defaults.UTXO_SELECTION_MAX_SINGLE_UTXO_FACTOR + (baseTxpFee + feePerInput); var bigInputThreshold = txpAmount * Defaults.UTXO_SELECTION_MAX_SINGLE_UTXO_FACTOR + (baseTxpFee + feePerInput);
@ -1427,6 +1433,7 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) {
var total = 0; var total = 0;
var selected = []; var selected = [];
var error;
_.each(smallInputs, function(input, i) { _.each(smallInputs, function(input, i) {
log.debug('Input #' + i + ': ' + formatInputs(input)); log.debug('Input #' + i + ': ' + formatInputs(input));
@ -1444,6 +1451,8 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) {
var txpSize = baseTxpSize + selected.length * sizePerInput; var txpSize = baseTxpSize + selected.length * sizePerInput;
var txpFee = baseTxpFee + selected.length * feePerInput; var txpFee = baseTxpFee + selected.length * feePerInput;
log.debug('Tx size: ' + formatSize(txpSize) + ', Tx fee: ' + formatAmount(txpFee));
var amountVsFeeRatio = txpFee / txpAmount; var amountVsFeeRatio = txpFee / txpAmount;
var singleInputFeeVsFeeRatio = txpFee / (baseTxpFee + feePerInput); var singleInputFeeVsFeeRatio = txpFee / (baseTxpFee + feePerInput);
var amountVsUtxoRatio = inputAmount / txpAmount; var amountVsUtxoRatio = inputAmount / txpAmount;
@ -1452,6 +1461,12 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) {
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('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) + ')'); log.debug('Tx amount/input amount:' + formatRatio(amountVsUtxoRatio) + ' (min: ' + formatRatio(Defaults.UTXO_SELECTION_MIN_TX_AMOUNT_VS_UTXO_FACTOR) + ')');
if (txpSize / 1000. > Defaults.MAX_TX_SIZE_IN_KB) {
log.debug('Breaking because tx size (' + formatSize(txpSize) + ') is too big (max: ' + formatSize(Defaults.MAX_TX_SIZE_IN_KB * 1000.) + ')'); // TODO
error = Errors.TX_MAX_SIZE_EXCEEDED;
return false;
}
if (!_.isEmpty(bigInputs)) { if (!_.isEmpty(bigInputs)) {
if ((amountVsFeeRatio > Defaults.UTXO_SELECTION_MAX_TX_AMOUNT_VS_FEE_FACTOR && if ((amountVsFeeRatio > Defaults.UTXO_SELECTION_MAX_TX_AMOUNT_VS_FEE_FACTOR &&
singleInputFeeVsFeeRatio > Defaults.UTXO_SELECTION_MAX_FEE_VS_SINGLE_UTXO_FEE_FACTOR)) { singleInputFeeVsFeeRatio > Defaults.UTXO_SELECTION_MAX_FEE_VS_SINGLE_UTXO_FEE_FACTOR)) {
@ -1463,12 +1478,6 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) {
log.debug('Breaking because utxo is too small compared to tx amount'); log.debug('Breaking because utxo is too small compared to tx amount');
return false; return false;
} }
if (txpSize / 1000. > Defaults.MAX_TX_SIZE_IN_KB) {
log.debug('Breaking because tx size is too big (' + formatSize(txpSize) + ')'); // TODO
return false;
}
} }
total += inputAmount; total += inputAmount;
@ -1489,11 +1498,9 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) {
} }
} }
if (!_.isEmpty(selected)) { if (_.isEmpty(selected)) {
var lockedOverhead = total - txpAmount; log.debug('Could not find enough funds within this utxo subset');
log.debug('SUCCESS! Total locked: ' + formatAmount(total) + ', overhead: ' + formatAmount(lockedOverhead) + ' (' + formatRatio(lockedOverhead / txpAmount) + ')'); return error || Errors.INSUFFICIENT_FUNDS_FOR_FEE;
} else {
log.debug('Could not find enough funds within this utxso subset');
} }
return selected; return selected;
@ -1531,6 +1538,7 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) {
var inputs = []; var inputs = [];
var groups = [6, 1, 0]; var groups = [6, 1, 0];
var error;
var lastGroupLength; var lastGroupLength;
_.each(groups, function(group) { _.each(groups, function(group) {
var candidateUtxos = _.filter(utxos, function(utxo) { var candidateUtxos = _.filter(utxos, function(utxo) {
@ -1549,20 +1557,25 @@ WalletService.prototype._selectTxInputs2 = function(txp, utxosToExclude, cb) {
lastGroupLength = candidateUtxos.length; lastGroupLength = candidateUtxos.length;
inputs = select(candidateUtxos); var result = select(candidateUtxos);
if (result && !_.isArray(result)) {
error = result;
} else {
inputs = result;
error = null;
}
log.debug('Selected inputs from this group: ' + formatInputs(inputs)); log.debug('Selected inputs from this group: ' + formatInputs(inputs));
if (!_.isEmpty(inputs)) return false; if (!_.isEmpty(inputs)) return false;
}); });
if (_.isEmpty(inputs)) return cb(Errors.INSUFFICIENT_FUNDS_FOR_FEE); if (error) return cb(error);
txp.setInputs(inputs); txp.setInputs(inputs);
if (txp.getEstimatedSize() / 1000 > Defaults.MAX_TX_SIZE_IN_KB)
return cb(Errors.TX_MAX_SIZE_EXCEEDED);
var err = self._checkTxAndEstimateFee(txp); var err = self._checkTxAndEstimateFee(txp);
if (!err) { if (!err) {
log.debug('Successfully built transaction. Total fees: ', formatAmount(txp.fee)); log.debug('Successfully built transaction. Total fees: ', formatAmount(txp.fee));
} else { } else {

6
test/integration/server.js

@ -2132,6 +2132,7 @@ describe('Wallet service', function() {
}); });
it('should use unconfirmed utxos only when no more confirmed utxos are available', function(done) { it('should use unconfirmed utxos only when no more confirmed utxos are available', function(done) {
// log.level = 'debug';
helpers.stubUtxos(server, wallet, [1.3, 'u0.5', 'u0.1', 1.2], function(utxos) { helpers.stubUtxos(server, wallet, [1.3, 'u0.5', 'u0.1', 1.2], function(utxos) {
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 2.55, TestData.copayers[0].privKey_1H_0, { var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 2.55, TestData.copayers[0].privKey_1H_0, {
message: 'some message' message: 'some message'
@ -2354,10 +2355,11 @@ describe('Wallet service', function() {
}); });
it('should fail to create a tx exceeding max size in kb', function(done) { it('should fail to create a tx exceeding max size in kb', function(done) {
helpers.stubUtxos(server, wallet, _.range(1, 10, 0), function() { log.level = 'debug';
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 8, TestData.copayers[0].privKey_1H_0);
var _oldDefault = Defaults.MAX_TX_SIZE_IN_KB; var _oldDefault = Defaults.MAX_TX_SIZE_IN_KB;
Defaults.MAX_TX_SIZE_IN_KB = 1; Defaults.MAX_TX_SIZE_IN_KB = 1;
helpers.stubUtxos(server, wallet, _.range(1, 10, 0), function() {
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 8, TestData.copayers[0].privKey_1H_0);
server.createTxLegacy(txOpts, function(err, tx) { server.createTxLegacy(txOpts, function(err, tx) {
should.exist(err); should.exist(err);
err.code.should.equal('TX_MAX_SIZE_EXCEEDED'); err.code.should.equal('TX_MAX_SIZE_EXCEEDED');

Loading…
Cancel
Save