|
|
@ -1153,6 +1153,7 @@ WalletService.prototype.getBalance = function(opts, cb) { |
|
|
|
* @param {Object} opts |
|
|
|
* @param {string} opts.feePerKb - The fee per KB used to compute the TX. |
|
|
|
* @param {string} opts.excludeUnconfirmedUtxos[=false] - Optional. Do not use UTXOs of unconfirmed transactions as inputs |
|
|
|
* @param {string} opts.returnInputs[=false] - Optional. Return the list of UTXOs that would be included in the tx. |
|
|
|
* @returns {Object} sendMaxInfo |
|
|
|
*/ |
|
|
|
WalletService.prototype.getSendMaxInfo = function(opts, cb) { |
|
|
@ -1209,7 +1210,7 @@ WalletService.prototype.getSendMaxInfo = function(opts, cb) { |
|
|
|
info.size = txp.getEstimatedSize(); |
|
|
|
info.fee = txp.getEstimatedFee(); |
|
|
|
info.amount = _.sum(txp.inputs, 'satoshis') - info.fee; |
|
|
|
info.inputs = txp.inputs; |
|
|
|
if (opts.returnInputs) info.inputs = txp.inputs; |
|
|
|
|
|
|
|
return cb(null, info); |
|
|
|
}); |
|
|
@ -1785,20 +1786,78 @@ WalletService.prototype.createTxLegacy = function(opts, cb) { |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
WalletService.prototype._doCreateTx = function(opts, cb) { |
|
|
|
WalletService.prototype._validateAndSanitizeTxOpts = function(wallet, opts, cb) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
if (!Utils.checkRequired(opts, ['outputs'])) |
|
|
|
return cb(new ClientError('Required argument missing')); |
|
|
|
async.series([ |
|
|
|
|
|
|
|
// feePerKb is required unless inputs & fee are specified
|
|
|
|
if (!_.isNumber(opts.feePerKb) && !(opts.inputs && _.isNumber(opts.fee))) |
|
|
|
return cb(new ClientError('Required argument missing')); |
|
|
|
function(next) { |
|
|
|
if (!Utils.checkRequired(opts, ['outputs'])) |
|
|
|
return next(new ClientError('Required argument missing')); |
|
|
|
next(); |
|
|
|
}, |
|
|
|
function(next) { |
|
|
|
// feePerKb is required unless inputs & fee are specified
|
|
|
|
if (!_.isNumber(opts.feePerKb) && !(opts.inputs && _.isNumber(opts.fee))) |
|
|
|
return next(new ClientError('Required argument missing')); |
|
|
|
|
|
|
|
if (_.isNumber(opts.feePerKb)) { |
|
|
|
if (opts.feePerKb < Defaults.MIN_FEE_PER_KB || opts.feePerKb > Defaults.MAX_FEE_PER_KB) |
|
|
|
return cb(new ClientError('Invalid fee per KB')); |
|
|
|
} |
|
|
|
if (_.isNumber(opts.feePerKb)) { |
|
|
|
if (opts.feePerKb < Defaults.MIN_FEE_PER_KB || opts.feePerKb > Defaults.MAX_FEE_PER_KB) |
|
|
|
return next(new ClientError('Invalid fee per KB')); |
|
|
|
} |
|
|
|
next(); |
|
|
|
}, |
|
|
|
function(next) { |
|
|
|
if (!opts.sendMax) return next(); |
|
|
|
if (!_.isArray(opts.outputs) || opts.outputs.length > 1) { |
|
|
|
return next(new ClientError('Only one output allowed when sendMax is specified')); |
|
|
|
} |
|
|
|
if (_.isNumber(opts.outputs[0].amount)) |
|
|
|
return next(new ClientError('Amount is not allowed when sendMax is specified')); |
|
|
|
if (_.isNumber(opts.fee)) |
|
|
|
return next(new ClientError('Fee is not allowed when sendMax is specified (use feePerKb instead)')); |
|
|
|
|
|
|
|
self.getSendMaxInfo({ |
|
|
|
feePerKb: opts.feePerKb || Defaults.DEFAULT_FEE_PER_KB, |
|
|
|
excludeUnconfirmedUtxos: !!opts.excludeUnconfirmedUtxos, |
|
|
|
returnInputs: true, |
|
|
|
}, function(err, info) { |
|
|
|
if (err) return next(err); |
|
|
|
opts.outputs[0].amount = info.amount; |
|
|
|
opts.inputs = info.inputs; |
|
|
|
return next(); |
|
|
|
}); |
|
|
|
}, |
|
|
|
function(next) { |
|
|
|
if (opts.validateOutputs === false) return next(); |
|
|
|
var validationError = self._validateOutputs(opts, wallet); |
|
|
|
if (validationError) { |
|
|
|
return next(validationError); |
|
|
|
} |
|
|
|
next(); |
|
|
|
}, |
|
|
|
], cb); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* Creates a new transaction proposal. |
|
|
|
* @param {Object} opts |
|
|
|
* @param {Array} opts.outputs - List of outputs. |
|
|
|
* @param {string} opts.outputs[].toAddress - Destination address. |
|
|
|
* @param {number} opts.outputs[].amount - Amount to transfer in satoshi. |
|
|
|
* @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.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.sendMax - Optional. Send maximum amount of funds that make sense under the specified fee/feePerKb conditions. (defaults to false). |
|
|
|
* @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. |
|
|
|
* @returns {TxProposal} Transaction proposal. |
|
|
|
*/ |
|
|
|
WalletService.prototype.createTx = function(opts, cb) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
self._runLocked(cb, function(cb) { |
|
|
|
|
|
|
@ -1813,17 +1872,13 @@ WalletService.prototype._doCreateTx = function(opts, cb) { |
|
|
|
next(); |
|
|
|
}); |
|
|
|
}, |
|
|
|
function(next) { |
|
|
|
self._validateAndSanitizeTxOpts(wallet, opts, next); |
|
|
|
}, |
|
|
|
function(next) { |
|
|
|
self._canCreateTx(function(err, canCreate) { |
|
|
|
if (err) return next(err); |
|
|
|
if (!canCreate) return next(Errors.TX_CANNOT_CREATE); |
|
|
|
|
|
|
|
if (opts.validateOutputs !== false) { |
|
|
|
var validationError = self._validateOutputs(opts, wallet); |
|
|
|
if (validationError) { |
|
|
|
return next(validationError); |
|
|
|
} |
|
|
|
} |
|
|
|
next(); |
|
|
|
}); |
|
|
|
}, |
|
|
@ -1869,53 +1924,6 @@ WalletService.prototype._doCreateTx = function(opts, cb) { |
|
|
|
}); |
|
|
|
}; |
|
|
|
|
|
|
|
/** |
|
|
|
* Creates a new transaction proposal. |
|
|
|
* @param {Object} opts |
|
|
|
* @param {Array} opts.outputs - List of outputs. |
|
|
|
* @param {string} opts.outputs[].toAddress - Destination address. |
|
|
|
* @param {number} opts.outputs[].amount - Amount to transfer in satoshi. |
|
|
|
* @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.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.sendMax - Optional. Send maximum amount of funds that make sense under the specified fee/feePerKb conditions. (defaults to false). |
|
|
|
* @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. |
|
|
|
* @returns {TxProposal} Transaction proposal. |
|
|
|
*/ |
|
|
|
WalletService.prototype.createTx = function(opts, cb) { |
|
|
|
var self = this; |
|
|
|
|
|
|
|
opts = opts || []; |
|
|
|
async.series([ |
|
|
|
|
|
|
|
function(next) { |
|
|
|
if (!opts.sendMax) return next(); |
|
|
|
if (!_.isArray(opts.outputs) || opts.outputs.length > 1) { |
|
|
|
return next(new ClientError('Only one output allowed when sendMax is specified')); |
|
|
|
} |
|
|
|
if (_.isNumber(opts.outputs[0].amount)) |
|
|
|
return next(new ClientError('Amount is not allowed when sendMax is specified')); |
|
|
|
if (_.isNumber(opts.fee)) |
|
|
|
return next(new ClientError('Fee is not allowed when sendMax is specified (use feePerKb instead)')); |
|
|
|
|
|
|
|
self.getSendMaxInfo(opts, function(err, info) { |
|
|
|
if (err) return next(err); |
|
|
|
opts.outputs[0].amount = info.amount; |
|
|
|
opts.inputs = info.inputs; |
|
|
|
return next(); |
|
|
|
}); |
|
|
|
}, |
|
|
|
], function(err) { |
|
|
|
if (err) return cb(err); |
|
|
|
self._doCreateTx(opts, cb); |
|
|
|
}); |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
WalletService.prototype._verifyRequestPubKey = function(requestPubKey, signature, xPubKey) { |
|
|
|
var pub = (new Bitcore.HDPublicKey(xPubKey)).derive(Constants.PATHS.REQUEST_KEY_AUTH).publicKey; |
|
|
|
return Utils.verifyMessage(requestPubKey, signature, pub.toString()); |
|
|
|