|
@ -827,41 +827,28 @@ WalletService.prototype._canCreateTx = function(copayerId, cb) { |
|
|
WalletService.prototype.createTx = function(opts, cb) { |
|
|
WalletService.prototype.createTx = function(opts, cb) { |
|
|
var self = this; |
|
|
var self = this; |
|
|
|
|
|
|
|
|
if (!Utils.checkRequired(opts, ['proposalSignature'])) |
|
|
if (!opts.outputs) { |
|
|
|
|
|
opts.outputs = _.pick(opts, ['amount', 'toAddress', 'message']); |
|
|
|
|
|
} |
|
|
|
|
|
opts.outputs = [].concat(opts.outputs); |
|
|
|
|
|
|
|
|
|
|
|
if (!Utils.checkRequired(opts, ['outputs', 'proposalSignature'])) |
|
|
return cb(new ClientError('Required argument missing')); |
|
|
return cb(new ClientError('Required argument missing')); |
|
|
|
|
|
|
|
|
opts.type = opts.type || Model.TxProposal.Types.SIMPLE; |
|
|
opts.type = opts.type || Model.TxProposal.Types.SIMPLE; |
|
|
if (!Model.TxProposal.isTypeSupported(opts.type)) |
|
|
if (!Model.TxProposal.isTypeSupported(opts.type)) |
|
|
return cb(new ClientError('Invalid proposal type')); |
|
|
return cb(new ClientError('Invalid proposal type')); |
|
|
|
|
|
|
|
|
if (opts.type == Model.TxProposal.Types.MULTIPLEOUTPUTS) { |
|
|
_.each(opts.outputs, function(output) { |
|
|
if (!Utils.checkRequired(opts, ['outputs'])) |
|
|
if (!Utils.checkRequired(output, ['toAddress', 'amount'])) { |
|
|
return cb(new ClientError('Required argument missing')); |
|
|
output.valid = false; |
|
|
_.each(opts.outputs, function(o) { |
|
|
cb(new ClientError('Required outputs argument missing')); |
|
|
o.valid = true; |
|
|
return false; |
|
|
if (!Utils.checkRequired(o, ['toAddress', 'amount'])) { |
|
|
} |
|
|
o.valid = false; |
|
|
}); |
|
|
cb(new ClientError('Required outputs argument missing')); |
|
|
if (_.any(opts.outputs, { |
|
|
return false; |
|
|
valid: false |
|
|
} |
|
|
})) return; |
|
|
_.each(_.keys(o), function(key) { |
|
|
|
|
|
if (!_.contains(['toAddress', 'amount', 'message', 'valid'], key)) { |
|
|
|
|
|
o.valid = false; |
|
|
|
|
|
cb(new ClientError('Invalid outputs argument found')); |
|
|
|
|
|
return false; |
|
|
|
|
|
} |
|
|
|
|
|
}); |
|
|
|
|
|
if (!o.valid) return false; |
|
|
|
|
|
}); |
|
|
|
|
|
if (_.any(opts.outputs, 'valid', false)) return; |
|
|
|
|
|
_.each(opts.outputs, function(o) { |
|
|
|
|
|
delete o.valid; |
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
if (!Utils.checkRequired(opts, ['toAddress', 'amount'])) |
|
|
|
|
|
return cb(new ClientError('Required argument missing')); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
var feePerKb = opts.feePerKb || 10000; |
|
|
var feePerKb = opts.feePerKb || 10000; |
|
|
if (feePerKb < WalletUtils.MIN_FEE_PER_KB || feePerKb > WalletUtils.MAX_FEE_PER_KB) |
|
|
if (feePerKb < WalletUtils.MIN_FEE_PER_KB || feePerKb > WalletUtils.MAX_FEE_PER_KB) |
|
@ -878,17 +865,7 @@ WalletService.prototype.createTx = function(opts, cb) { |
|
|
if (!canCreate) |
|
|
if (!canCreate) |
|
|
return cb(new ClientError('NOTALLOWEDTOCREATETX', 'Cannot create TX proposal during backoff time')); |
|
|
return cb(new ClientError('NOTALLOWEDTOCREATETX', 'Cannot create TX proposal during backoff time')); |
|
|
|
|
|
|
|
|
var copayer = wallet.getCopayer(self.copayerId); |
|
|
_.each(opts.outputs, function(output) { |
|
|
var proposalHeader = Model.TxProposal.create(opts).getHeader(); |
|
|
|
|
|
var hash = WalletUtils.getProposalHash(proposalHeader); |
|
|
|
|
|
if (!self._verifySignature(hash, opts.proposalSignature, copayer.requestPubKey)) |
|
|
|
|
|
return cb(new ClientError('Invalid proposal signature')); |
|
|
|
|
|
|
|
|
|
|
|
var outputs = (opts.type == Model.TxProposal.Types.MULTIPLEOUTPUTS) ? opts.outputs : [{ |
|
|
|
|
|
toAddress: opts.toAddress, |
|
|
|
|
|
amount: opts.amount |
|
|
|
|
|
}]; |
|
|
|
|
|
_.each(outputs, function(output) { |
|
|
|
|
|
output.valid = false; |
|
|
output.valid = false; |
|
|
var toAddress = {}; |
|
|
var toAddress = {}; |
|
|
try { |
|
|
try { |
|
@ -901,7 +878,7 @@ WalletService.prototype.createTx = function(opts, cb) { |
|
|
cb(new ClientError('INVALIDADDRESS', 'Incorrect address network')); |
|
|
cb(new ClientError('INVALIDADDRESS', 'Incorrect address network')); |
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
|
if (output.amount <= 0) { |
|
|
if (!_.isNumber(output.amount) || _.isNaN(output.amount) || output.amount <= 0) { |
|
|
cb(new ClientError('Invalid amount')); |
|
|
cb(new ClientError('Invalid amount')); |
|
|
return false; |
|
|
return false; |
|
|
} |
|
|
} |
|
@ -911,9 +888,9 @@ WalletService.prototype.createTx = function(opts, cb) { |
|
|
} |
|
|
} |
|
|
output.valid = true; |
|
|
output.valid = true; |
|
|
}); |
|
|
}); |
|
|
if (_.any(outputs, 'valid', false)) return; |
|
|
if (_.any(opts.outputs, { |
|
|
|
|
|
valid: false |
|
|
var changeAddress = wallet.createAddress(true); |
|
|
})) return; |
|
|
|
|
|
|
|
|
var txp = Model.TxProposal.create({ |
|
|
var txp = Model.TxProposal.create({ |
|
|
type: opts.type, |
|
|
type: opts.type, |
|
@ -926,18 +903,23 @@ WalletService.prototype.createTx = function(opts, cb) { |
|
|
proposalSignature: opts.proposalSignature, |
|
|
proposalSignature: opts.proposalSignature, |
|
|
feePerKb: feePerKb, |
|
|
feePerKb: feePerKb, |
|
|
payProUrl: opts.payProUrl, |
|
|
payProUrl: opts.payProUrl, |
|
|
changeAddress: changeAddress, |
|
|
|
|
|
requiredSignatures: wallet.m, |
|
|
requiredSignatures: wallet.m, |
|
|
requiredRejections: Math.min(wallet.m, wallet.n - wallet.m + 1), |
|
|
requiredRejections: Math.min(wallet.m, wallet.n - wallet.m + 1), |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
var copayer = wallet.getCopayer(self.copayerId); |
|
|
|
|
|
var hash = WalletUtils.getProposalHash(txp.getHeader()); |
|
|
|
|
|
if (!self._verifySignature(hash, opts.proposalSignature, copayer.requestPubKey)) |
|
|
|
|
|
return cb(new ClientError('Invalid proposal signature')); |
|
|
|
|
|
|
|
|
|
|
|
txp.changeAddress = wallet.createAddress(true); |
|
|
|
|
|
|
|
|
self._selectTxInputs(txp, function(err) { |
|
|
self._selectTxInputs(txp, function(err) { |
|
|
if (err) return cb(err); |
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
|
$.checkState(txp.inputs); |
|
|
$.checkState(txp.inputs); |
|
|
|
|
|
|
|
|
self.storage.storeAddressAndWallet(wallet, changeAddress, function(err) { |
|
|
self.storage.storeAddressAndWallet(wallet, txp.changeAddress, function(err) { |
|
|
if (err) return cb(err); |
|
|
if (err) return cb(err); |
|
|
|
|
|
|
|
|
self.storage.storeTx(wallet.id, txp, function(err) { |
|
|
self.storage.storeTx(wallet.id, txp, function(err) { |
|
|