Skip to content
This repository has been archived by the owner on Apr 3, 2019. It is now read-only.

Commit

Permalink
Merge pull request #483 from isocolsky/ref/send-max
Browse files Browse the repository at this point in the history
Improve response in send max API
  • Loading branch information
matiu committed Mar 21, 2016
2 parents 93a8d65 + 6d8b4e7 commit d93630b
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 14 deletions.
39 changes: 29 additions & 10 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -1177,13 +1177,19 @@ WalletService.prototype.getSendMaxInfo = function(opts, cb) {
amount: 0,
fee: 0,
inputs: [],
utxosBelowFee: 0,
amountBelowFee: 0,
utxosAboveMaxSize: 0,
amountAboveMaxSize: 0,
};

var inputs = _.reject(utxos, 'locked');
if (!!opts.excludeUnconfirmedUtxos) {
inputs = _.filter(inputs, 'confirmations');
}
inputs = _.sortBy(inputs, 'satoshis');
inputs = _.sortBy(inputs, function(input) {
return -input.satoshis;
});

if (_.isEmpty(inputs)) return cb(null, info);

Expand All @@ -1195,18 +1201,31 @@ WalletService.prototype.getSendMaxInfo = function(opts, cb) {
feePerKb: opts.feePerKb,
});

var lastFee = txp.getEstimatedFee();
_.eachRight(inputs, function(input) {
txp.inputs.push(input);
var fee = txp.getEstimatedFee();
var sizeInKb = txp.getEstimatedSize() / 1000.;
if (fee - lastFee > input.satoshis || sizeInKb > Defaults.MAX_TX_SIZE_IN_KB) {
txp.inputs.pop();
var baseTxpSize = txp.getEstimatedSize();
var baseTxpFee = baseTxpSize * txp.feePerKb / 1000.;
var sizePerInput = txp.getEstimatedSizeForSingleInput();
var feePerInput = sizePerInput * txp.feePerKb / 1000.;

var partitionedByAmount = _.partition(inputs, function(input) {
return input.satoshis > feePerInput;
});

info.utxosBelowFee = partitionedByAmount[1].length;
info.amountBelowFee = _.sum(partitionedByAmount[1], 'satoshis');
inputs = partitionedByAmount[0];

_.each(inputs, function(input, i) {
var sizeInKb = (baseTxpSize + (i + 1) * sizePerInput) / 1000.;
if (sizeInKb > Defaults.MAX_TX_SIZE_IN_KB) {
info.utxosAboveMaxSize = inputs.length - i;
info.amountAboveMaxSize = _.sum(_.slice(inputs, i), 'satoshis');
return false;
}

lastFee = fee;
txp.inputs.push(input);
});

if (_.isEmpty(txp.inputs)) return cb(null, info);

info.size = txp.getEstimatedSize();
info.fee = txp.getEstimatedFee();
info.amount = _.sum(txp.inputs, 'satoshis') - info.fee;
Expand Down
39 changes: 35 additions & 4 deletions test/integration/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -3696,7 +3696,7 @@ describe('Wallet service', function() {
});
};

it('should be able to get send max info on empty wallet', function(done) {
it('should be able to get send max info on 0 utxo wallet', function(done) {
server.getSendMaxInfo({
feePerKb: 10000,
returnInputs: true,
Expand All @@ -3707,6 +3707,10 @@ describe('Wallet service', function() {
info.amount.should.equal(0);
info.fee.should.equal(0);
info.inputs.should.be.empty;
info.utxosBelowFee.should.equal(0);
info.amountBelowFee.should.equal(0);
info.utxosAboveMaxSize.should.equal(0);
info.amountAboveMaxSize.should.equal(0);
done();
});
});
Expand All @@ -3722,6 +3726,10 @@ describe('Wallet service', function() {
info.size.should.equal(1304);
info.fee.should.equal(info.size * 10000 / 1000.);
info.amount.should.equal(1e8 - info.fee);
info.utxosBelowFee.should.equal(0);
info.amountBelowFee.should.equal(0);
info.utxosAboveMaxSize.should.equal(0);
info.amountAboveMaxSize.should.equal(0);
sendTx(info, done);
});
});
Expand All @@ -3745,7 +3753,6 @@ describe('Wallet service', function() {
});
});
});

it('should exclude unconfirmed inputs', function(done) {
helpers.stubUtxos(server, wallet, ['u0.1', 0.2, 0.3, 0.4], function() {
server.getSendMaxInfo({
Expand Down Expand Up @@ -3791,7 +3798,7 @@ describe('Wallet service', function() {
});
});
it('should ignore utxos not contributing to total amount (below their cost in fee)', function(done) {
helpers.stubUtxos(server, wallet, ['u0.1', 0.2, 0.3, 0.4, 0.000001, 0.0002, 0.0003], function() {
helpers.stubUtxos(server, wallet, ['u0.1', 0.2, 0.3, 0.4, '1bit', '100bit', '200bit'], function() {
server.getSendMaxInfo({
feePerKb: 0.001e8,
returnInputs: true,
Expand All @@ -3802,6 +3809,8 @@ describe('Wallet service', function() {
info.size.should.equal(1304);
info.fee.should.equal(info.size * 0.001e8 / 1000.);
info.amount.should.equal(1e8 - info.fee);
info.utxosBelowFee.should.equal(3);
info.amountBelowFee.should.equal(301e2);
server.getSendMaxInfo({
feePerKb: 0.0001e8,
returnInputs: true,
Expand All @@ -3811,12 +3820,32 @@ describe('Wallet service', function() {
info.inputs.length.should.equal(6);
info.size.should.equal(1907);
info.fee.should.equal(info.size * 0.0001e8 / 1000.);
info.amount.should.equal(1.0005e8 - info.fee);
info.amount.should.equal(1.0003e8 - info.fee);
info.utxosBelowFee.should.equal(1);
info.amountBelowFee.should.equal(1e2);
sendTx(info, done);
});
});
});
});
it('should work when all inputs are below their cost in fee', function(done) {
helpers.stubUtxos(server, wallet, ['u 10bit', '10bit', '20bit'], function() {
server.getSendMaxInfo({
feePerKb: 500e2,
returnInputs: true,
}, function(err, info) {
should.not.exist(err);
should.exist(info);
info.inputs.should.be.empty;
info.size.should.equal(0);
info.fee.should.equal(0);
info.amount.should.equal(0);
info.utxosBelowFee.should.equal(3);
info.amountBelowFee.should.equal(40e2);
done();
});
});
});
it('should not go beyond max tx size', function(done) {
var _oldDefault = Defaults.MAX_TX_SIZE_IN_KB;
Defaults.MAX_TX_SIZE_IN_KB = 2;
Expand All @@ -3829,6 +3858,8 @@ describe('Wallet service', function() {
should.exist(info);
info.size.should.be.below(2000);
info.inputs.length.should.be.below(9);
info.utxosAboveMaxSize.should.equal(3);
info.amountAboveMaxSize.should.equal(3e8);
Defaults.MAX_TX_SIZE_IN_KB = _oldDefault;
sendTx(info, done);
});
Expand Down

0 comments on commit d93630b

Please sign in to comment.