Merge pull request #484 from isocolsky/fix/utxo-selection
Add threshold for change creation when selecting UTXOs
This commit is contained in:
commit
68ec16d35a
|
@ -63,4 +63,7 @@ Defaults.UTXO_SELECTION_MAX_FEE_VS_TX_AMOUNT_FACTOR = 0.05;
|
||||||
// when fees are significant (proportional to how much we would pay for using that big input only).
|
// when fees are significant (proportional to how much we would pay for using that big input only).
|
||||||
Defaults.UTXO_SELECTION_MAX_FEE_VS_SINGLE_UTXO_FEE_FACTOR = 5;
|
Defaults.UTXO_SELECTION_MAX_FEE_VS_SINGLE_UTXO_FEE_FACTOR = 5;
|
||||||
|
|
||||||
|
// Do not generate change for less than the specified amount
|
||||||
|
Defaults.UTXO_SELECTION_MIN_CHANGE_AMOUNT = 5000;
|
||||||
|
|
||||||
module.exports = Defaults;
|
module.exports = Defaults;
|
||||||
|
|
|
@ -1480,8 +1480,9 @@ WalletService.prototype._selectTxInputs = function(txp, utxosToExclude, cb) {
|
||||||
var changeAmount = Math.round(total - txpAmount - fee);
|
var changeAmount = Math.round(total - txpAmount - fee);
|
||||||
log.debug('Tx change: ', Utils.formatAmountInBtc(changeAmount));
|
log.debug('Tx change: ', Utils.formatAmountInBtc(changeAmount));
|
||||||
|
|
||||||
if (changeAmount > 0 && changeAmount <= Bitcore.Transaction.DUST_AMOUNT) {
|
var smallChangeThreshold = Math.max(Defaults.UTXO_SELECTION_MIN_CHANGE_AMOUNT, Bitcore.Transaction.DUST_AMOUNT);
|
||||||
log.debug('Change below dust amount (' + Utils.formatAmountInBtc(Bitcore.Transaction.DUST_AMOUNT) + ')');
|
if (changeAmount > 0 && changeAmount <= smallChangeThreshold) {
|
||||||
|
log.debug('Change below threshold (' + Utils.formatAmountInBtc(smallChangeThreshold) + '). Incrementing fee to remove change.');
|
||||||
// Remove dust change by incrementing fee
|
// Remove dust change by incrementing fee
|
||||||
fee += changeAmount;
|
fee += changeAmount;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2326,18 +2326,18 @@ describe('Wallet service', function() {
|
||||||
|
|
||||||
it('should be possible to use a smaller fee', function(done) {
|
it('should be possible to use a smaller fee', function(done) {
|
||||||
helpers.stubUtxos(server, wallet, 1, function() {
|
helpers.stubUtxos(server, wallet, 1, function() {
|
||||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.99995, TestData.copayers[0].privKey_1H_0, {
|
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.9999, TestData.copayers[0].privKey_1H_0, {
|
||||||
feePerKb: 80000
|
feePerKb: 80000
|
||||||
});
|
});
|
||||||
server.createTxLegacy(txOpts, function(err, tx) {
|
server.createTxLegacy(txOpts, function(err, tx) {
|
||||||
should.exist(err);
|
should.exist(err);
|
||||||
err.code.should.equal('INSUFFICIENT_FUNDS_FOR_FEE');
|
err.code.should.equal('INSUFFICIENT_FUNDS_FOR_FEE');
|
||||||
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.99995, TestData.copayers[0].privKey_1H_0, {
|
var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', 0.9999, TestData.copayers[0].privKey_1H_0, {
|
||||||
feePerKb: 5000
|
feePerKb: 5000
|
||||||
});
|
});
|
||||||
server.createTxLegacy(txOpts, function(err, tx) {
|
server.createTxLegacy(txOpts, function(err, tx) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
var estimatedFee = 5000 * 400 / 1000; // fully signed tx should have about 400 bytes
|
var estimatedFee = 5000 * 410 / 1000; // fully signed tx should have about 410 bytes
|
||||||
tx.fee.should.be.within(0.9 * estimatedFee, 1.1 * estimatedFee);
|
tx.fee.should.be.within(0.9 * estimatedFee, 1.1 * estimatedFee);
|
||||||
|
|
||||||
// Sign it to make sure Bitcore doesn't complain about the fees
|
// Sign it to make sure Bitcore doesn't complain about the fees
|
||||||
|
@ -3597,17 +3597,18 @@ describe('Wallet service', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should correct fee if resulting change would be below dust', function(done) {
|
it('should correct fee if resulting change would be below threshold', function(done) {
|
||||||
helpers.stubUtxos(server, wallet, ['200bit', '500sat'], function() {
|
helpers.stubUtxos(server, wallet, ['200bit', '500sat'], function() {
|
||||||
var txOpts = {
|
var txOpts = {
|
||||||
outputs: [{
|
outputs: [{
|
||||||
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
|
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
|
||||||
amount: 200e2,
|
amount: 150e2,
|
||||||
}],
|
}],
|
||||||
feePerKb: 400,
|
feePerKb: 100e2,
|
||||||
};
|
};
|
||||||
server.createTx(txOpts, function(err, txp) {
|
server.createTx(txOpts, function(err, txp) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
|
txp.inputs.length.should.equal(1);
|
||||||
(_.sum(txp.inputs, 'satoshis') - txp.outputs[0].amount - txp.fee).should.equal(0);
|
(_.sum(txp.inputs, 'satoshis') - txp.outputs[0].amount - txp.fee).should.equal(0);
|
||||||
var changeOutput = txp.getBitcoreTx().getChangeOutput();
|
var changeOutput = txp.getBitcoreTx().getChangeOutput();
|
||||||
should.not.exist(changeOutput);
|
should.not.exist(changeOutput);
|
||||||
|
|
Loading…
Reference in New Issue