From 6f196aa1b998cd907ce5ef00dc5ad0d010e81ef1 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Thu, 28 Jan 2016 17:53:22 -0300 Subject: [PATCH] allow fee to be specified on proposal creation --- lib/common/defaults.js | 1 + lib/model/txproposal.js | 5 +++-- lib/server.js | 23 +++++++++++++++++------ test/integration/helpers.js | 2 +- test/integration/server.js | 19 +++++++++++++++++++ 5 files changed, 41 insertions(+), 9 deletions(-) diff --git a/lib/common/defaults.js b/lib/common/defaults.js index b5b366a..2b87252 100644 --- a/lib/common/defaults.js +++ b/lib/common/defaults.js @@ -5,6 +5,7 @@ var Defaults = {}; Defaults.DEFAULT_FEE_PER_KB = 10000; Defaults.MIN_FEE_PER_KB = 0; Defaults.MAX_FEE_PER_KB = 1000000; +Defaults.MIN_TX_FEE = 0; Defaults.MAX_TX_FEE = 1 * 1e8; Defaults.MAX_KEYS = 100; diff --git a/lib/model/txproposal.js b/lib/model/txproposal.js index f3747cc..2a3d115 100644 --- a/lib/model/txproposal.js +++ b/lib/model/txproposal.js @@ -44,7 +44,7 @@ TxProposal.create = function(opts) { x.requiredRejections = Math.min(x.walletM, x.walletN - x.walletM + 1), x.status = 'temporary'; x.actions = []; - x.fee = null; + x.fee = opts.fee; x.feePerKb = opts.feePerKb; x.excludeUnconfirmedUtxos = opts.excludeUnconfirmedUtxos; @@ -74,6 +74,7 @@ TxProposal.fromObj = function(obj) { x.id = obj.id; x.walletId = obj.walletId; x.creatorId = obj.creatorId; + x.network = obj.network; x.outputs = obj.outputs; x.amount = obj.amount; x.message = obj.message; @@ -93,7 +94,6 @@ TxProposal.fromObj = function(obj) { }); x.outputOrder = obj.outputOrder; x.fee = obj.fee; - x.network = obj.network; x.feePerKb = obj.feePerKb; x.excludeUnconfirmedUtxos = obj.excludeUnconfirmedUtxos; x.addressType = obj.addressType; @@ -232,6 +232,7 @@ TxProposal.prototype.getEstimatedSize = function() { }; TxProposal.prototype.estimateFee = function() { + $.checkState(_.isNumber(this.feePerKb)); var fee = this.feePerKb * this.getEstimatedSize() / 1000; this.fee = parseInt(fee.toFixed(0)); }; diff --git a/lib/server.js b/lib/server.js index dc7f857..e1ed48e 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1219,8 +1219,11 @@ WalletService.prototype._checkTxAndEstimateFee = function(txp) { serializationOpts.disableLargeFees = true; } - try { + if (_.isNumber(txp.feePerKb)) { txp.estimateFee(); + } + + try { var bitcoreTx = txp.getBitcoreTx(); bitcoreError = bitcoreTx.getSerializationError(serializationOpts); if (!bitcoreError) { @@ -1535,7 +1538,8 @@ WalletService.prototype.createTxLegacy = function(opts, cb) { * @param {string} opts.outputs[].message - A message to attach to this output. * @param {string} opts.message - A message to attach to this transaction. * @param {Array} opts.inputs - Optional. Inputs for this TX - * @param {string} opts.feePerKb - Optional. Use an alternative fee per KB for this TX + * @param {string} opts.fee - Optional. Use an alternative fee for this TX (mutually exclusive with feePerKb) + * @param {string} opts.feePerKb - Optional. Use an alternative fee per KB for this TX (mutually exclusive with fee) * @param {string} opts.payProUrl - Optional. Paypro URL for peers to verify TX * @param {string} opts.excludeUnconfirmedUtxos[=false] - Optional. Do not use UTXOs of unconfirmed transactions as inputs * @param {string} opts.validateOutputs[=true] - Optional. Perform validation on outputs. @@ -1547,9 +1551,15 @@ WalletService.prototype.createTx = function(opts, cb) { if (!Utils.checkRequired(opts, ['outputs'])) return cb(new ClientError('Required argument missing')); - var feePerKb = opts.feePerKb || Defaults.DEFAULT_FEE_PER_KB; - if (feePerKb < Defaults.MIN_FEE_PER_KB || feePerKb > Defaults.MAX_FEE_PER_KB) - return cb(new ClientError('Invalid fee per KB value')); + if (_.isNumber(opts.fee)) { + if (opts.fee < Defaults.MIN_FEE || opts.fee > Defaults.MAX_FEE) + return cb(new ClientError('Invalid fee')); + } else { + opts.fee = null; + opts.feePerKb = opts.feePerKb || Defaults.DEFAULT_FEE_PER_KB; + if (opts.feePerKb < Defaults.MIN_FEE_PER_KB || opts.feePerKb > Defaults.MAX_FEE_PER_KB) + return cb(new ClientError('Invalid fee per KB')); + } self._runLocked(cb, function(cb) { self.getWallet({}, function(err, wallet) { @@ -1574,7 +1584,8 @@ WalletService.prototype.createTx = function(opts, cb) { outputs: opts.outputs, message: opts.message, changeAddress: wallet.createAddress(true), - feePerKb: feePerKb, + fee: opts.fee, + feePerKb: opts.feePerKb, payProUrl: opts.payProUrl, walletM: wallet.m, walletN: wallet.n, diff --git a/test/integration/helpers.js b/test/integration/helpers.js index 99b43b9..957efd7 100644 --- a/test/integration/helpers.js +++ b/test/integration/helpers.js @@ -411,7 +411,7 @@ helpers.createProposalOpts2 = function(outputs, moreOpts, inputs) { }; if (moreOpts) { - moreOpts = _.pick(moreOpts, ['feePerKb', 'customData', 'message']); + moreOpts = _.pick(moreOpts, ['fee', 'feePerKb', 'customData', 'message']); opts = _.assign(opts, moreOpts); } diff --git a/test/integration/server.js b/test/integration/server.js index c2bbd67..94433c5 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -2721,6 +2721,25 @@ describe('Wallet service', function() { }); }); + it('should be able to specify the final fee', function(done) { + helpers.stubUtxos(server, wallet, [1, 2], function() { + var txOpts = helpers.createProposalOpts2([{ + toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', + amount: 0.8, + }], { + fee: 123400, + }); + server.createTx(txOpts, function(err, tx) { + should.not.exist(err); + should.exist(tx); + tx.fee.should.equal(123400); + var t = tx.getBitcoreTx(); + t.getFee().should.equal(123400); + done(); + }); + }); + }); + it('should be able to send a temporary tx proposal', function(done) { helpers.stubUtxos(server, wallet, [1, 2], function() { var txOpts = helpers.createProposalOpts2([{