diff --git a/lib/model/txproposal.js b/lib/model/txproposal.js index 3209246..5a05b00 100644 --- a/lib/model/txproposal.js +++ b/lib/model/txproposal.js @@ -171,10 +171,9 @@ TxProposal.prototype.getRawTx = function() { return t.uncheckedSerialize(); }; -TxProposal.prototype.estimateFee = function() { +TxProposal.prototype.getEstimatedSize = function() { // Note: found empirically based on all multisig P2SH inputs and within m & n allowed limits. var safetyMargin = 0.05; - var walletM = this.requiredSignatures; var overhead = 4 + 4 + 9 + 9; @@ -185,7 +184,13 @@ TxProposal.prototype.estimateFee = function() { var size = overhead + inputSize * nbInputs + outputSize * nbOutputs; - var fee = this.feePerKb * (size * (1 + safetyMargin)) / 1000; + return parseInt((size * (1 + safetyMargin)).toFixed(0)); +}; + +TxProposal.prototype.estimateFee = function() { + + var size = this.getEstimatedSize(); + var fee = this.feePerKb * size / 1000; // Round up to nearest bit this.fee = parseInt((Math.ceil(fee / 100) * 100).toFixed(0)); diff --git a/lib/server.js b/lib/server.js index 6672769..e4218bf 100644 --- a/lib/server.js +++ b/lib/server.js @@ -692,7 +692,7 @@ WalletService.prototype._totalizeUtxos = function(utxos) { }; -WalletService.prototype._computeKbToSendMax = function(utxos, amount, cb) { +WalletService.prototype._computeBytesToSendMax = function(utxos, cb) { var self = this; var unlockedUtxos = _.reject(utxos, 'locked'); @@ -701,17 +701,16 @@ WalletService.prototype._computeKbToSendMax = function(utxos, amount, cb) { self.getWallet({}, function(err, wallet) { if (err) return cb(err); - var t = WalletUtils.newBitcoreTransaction(); - try { - _.each(unlockedUtxos, function(i) { - t.from(i, i.publicKeys, wallet.m); - }); - t.to(utxos[0].address, amount); - var sizeInKb = Math.ceil(t._estimateSize() / 1000); - return cb(null, sizeInKb); - } catch (ex) { - return cb(ex); - } + var txp = Model.TxProposal.create({ + walletId: self.walletId, + requiredSignatures: wallet.m, + walletN: wallet.n, + }); + txp.inputs = unlockedUtxos; + + var size = txp.getEstimatedSize(); + + return cb(null, size); }); }; @@ -744,11 +743,11 @@ WalletService.prototype.getBalance = function(opts, cb) { balance.byAddress = _.values(byAddress); - self._computeKbToSendMax(utxos, balance.availableAmount, function(err, sizeInKb) { + self._computeBytesToSendMax(utxos, function(err, sizeInBytes) { if (err) { log.error('Could not compute fees needed to transfer max amount', err); } - balance.totalKbToSendMax = sizeInKb || 0; + balance.totalBytesToSendMax = sizeInBytes || 0; return cb(null, balance); }); }); diff --git a/test/integration/server.js b/test/integration/server.js index 6162f2d..6770ae0 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -1370,7 +1370,7 @@ describe('Wallet service', function() { balance.totalAmount.should.equal(helpers.toSatoshi(6)); balance.lockedAmount.should.equal(0); balance.availableAmount.should.equal(helpers.toSatoshi(6)); - balance.totalKbToSendMax.should.equal(1); + balance.totalBytesToSendMax.should.equal(578); balance.totalConfirmedAmount.should.equal(helpers.toSatoshi(4)); balance.lockedConfirmedAmount.should.equal(0); @@ -1396,7 +1396,7 @@ describe('Wallet service', function() { balance.totalAmount.should.equal(0); balance.lockedAmount.should.equal(0); balance.availableAmount.should.equal(0); - balance.totalKbToSendMax.should.equal(0); + balance.totalBytesToSendMax.should.equal(0); should.exist(balance.byAddress); balance.byAddress.length.should.equal(0); done(); @@ -1412,7 +1412,7 @@ describe('Wallet service', function() { balance.totalAmount.should.equal(0); balance.lockedAmount.should.equal(0); balance.availableAmount.should.equal(0); - balance.totalKbToSendMax.should.equal(0); + balance.totalBytesToSendMax.should.equal(0); should.exist(balance.byAddress); balance.byAddress.length.should.equal(0); done(); @@ -1440,7 +1440,7 @@ describe('Wallet service', function() { should.exist(balance); balance.totalAmount.should.equal(helpers.toSatoshi(9)); balance.lockedAmount.should.equal(0); - balance.totalKbToSendMax.should.equal(2); + balance.totalBytesToSendMax.should.equal(1535); done(); }); }); @@ -2093,18 +2093,21 @@ describe('Wallet service', function() { balance.totalAmount.should.equal(helpers.toSatoshi(9)); balance.lockedAmount.should.equal(0); balance.availableAmount.should.equal(helpers.toSatoshi(9)); - balance.totalKbToSendMax.should.equal(3); - var max = (balance.totalAmount - balance.lockedAmount) - (balance.totalKbToSendMax * 10000); + balance.totalBytesToSendMax.should.equal(2896); + var sizeInKB = balance.totalBytesToSendMax / 1000; + var fee = parseInt((Math.ceil(sizeInKB * 10000 / 100) * 100).toFixed(0)); + var max = balance.availableAmount - fee; var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', max / 1e8, null, TestData.copayers[0].privKey_1H_0); server.createTx(txOpts, function(err, tx) { should.not.exist(err); should.exist(tx); tx.amount.should.equal(max); - var estimatedFee = 2900 * 10000 / 1000; // estimated size = 2900 bytes + var estimatedFee = 2896 * 10000 / 1000; tx.fee.should.be.within(0.9 * estimatedFee, 1.1 * estimatedFee); server.getBalance({}, function(err, balance) { should.not.exist(err); balance.lockedAmount.should.equal(helpers.toSatoshi(9)); + balance.availableAmount.should.equal(0); done(); }); }); @@ -2121,14 +2124,16 @@ describe('Wallet service', function() { balance.totalAmount.should.equal(helpers.toSatoshi(9)); balance.lockedAmount.should.equal(helpers.toSatoshi(4)); balance.availableAmount.should.equal(helpers.toSatoshi(5)); - balance.totalKbToSendMax.should.equal(2); - var max = (balance.totalAmount - balance.lockedAmount) - (balance.totalKbToSendMax * 2000); + balance.totalBytesToSendMax.should.equal(1653); + var sizeInKB = balance.totalBytesToSendMax / 1000; + var fee = parseInt((Math.ceil(sizeInKB * 2000 / 100) * 100).toFixed(0)); + var max = balance.availableAmount - fee; var txOpts = helpers.createSimpleProposalOpts('18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', max / 1e8, null, TestData.copayers[0].privKey_1H_0, 2000); server.createTx(txOpts, function(err, tx) { should.not.exist(err); should.exist(tx); tx.amount.should.equal(max); - var estimatedFee = 1650 * 2000 / 1000; // estimated size = 1650 bytes + var estimatedFee = 1653 * 2000 / 1000; tx.fee.should.be.within(0.9 * estimatedFee, 1.1 * estimatedFee); server.getBalance({}, function(err, balance) { should.not.exist(err);