Browse Source

simplify validations on createTx

activeAddress
Ivan Socolsky 10 years ago
parent
commit
48e0709607
  1. 4
      lib/model/txproposal.js
  2. 74
      lib/server.js
  3. 10
      test/integration/server.js
  4. 9
      test/models/txproposal.js

4
lib/model/txproposal.js

@ -35,7 +35,9 @@ TxProposal._create.simple = function(txp, opts) {
TxProposal._create.undefined = TxProposal._create.simple; TxProposal._create.undefined = TxProposal._create.simple;
TxProposal._create.multiple_outputs = function(txp, opts) { TxProposal._create.multiple_outputs = function(txp, opts) {
txp.outputs = opts.outputs; txp.outputs = _.map(opts.outputs, function(output) {
return _.pick(output, ['amount', 'toAddress', 'message']);
});
txp.outputOrder = _.shuffle(_.range(txp.outputs.length + 1)); txp.outputOrder = _.shuffle(_.range(txp.outputs.length + 1));
try { try {
txp.network = Bitcore.Address(txp.outputs[0].toAddress).toObject().network; txp.network = Bitcore.Address(txp.outputs[0].toAddress).toObject().network;

74
lib/server.js

@ -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) {

10
test/integration/server.js

@ -137,7 +137,7 @@ helpers.toSatoshi = function(btc) {
helpers.stubUtxos = function(server, wallet, amounts, cb) { helpers.stubUtxos = function(server, wallet, amounts, cb) {
var amounts = [].concat(amounts); var amounts = [].concat(amounts);
async.map(_.range(1, Math.ceil(amounts.length / 2) + 1), function(i, next) { async.mapSeries(_.range(1, Math.ceil(amounts.length / 2) + 1), function(i, next) {
server.createAddress({}, function(err, address) { server.createAddress({}, function(err, address) {
next(err, address); next(err, address);
}); });
@ -248,7 +248,7 @@ helpers.createAddresses = function(server, wallet, main, change, cb) {
var storage, blockchainExplorer; var storage, blockchainExplorer;
var useMongo = false; var useMongo = true;
function initStorage(cb) { function initStorage(cb) {
function getDb(cb) { function getDb(cb) {
@ -1685,13 +1685,11 @@ describe('Wallet service', function() {
}); });
}); });
it('should fail to create tx for type multiple_outputs with invalid output argument', function(done) { it('should fail to create tx for type multiple_outputs with missing output argument', function(done) {
helpers.stubUtxos(server, wallet, [100, 200], function() { helpers.stubUtxos(server, wallet, [100, 200], function() {
var outputs = [{ var outputs = [{
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
amount: 80, amount: 80,
message: 'message #1', message: 'message #1',
foo: 'bar'
}, { }, {
toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7', toAddress: '18PzpUFkFZE8zKWUPvfykkTxmB9oMR8qP7',
amount: 90, amount: 90,
@ -1700,7 +1698,7 @@ describe('Wallet service', function() {
var txOpts = helpers.createProposalOptsByType(Model.TxProposal.Types.MULTIPLEOUTPUTS, outputs, 'some message', TestData.copayers[0].privKey_1H_0); var txOpts = helpers.createProposalOptsByType(Model.TxProposal.Types.MULTIPLEOUTPUTS, outputs, 'some message', TestData.copayers[0].privKey_1H_0);
server.createTx(txOpts, function(err, tx) { server.createTx(txOpts, function(err, tx) {
should.exist(err); should.exist(err);
err.message.should.contain('Invalid outputs argument'); err.message.should.contain('outputs argument missing');
done(); done();
}); });
}); });

9
test/models/txproposal.js

@ -64,18 +64,11 @@ describe('TXProposal', function() {
}); });
describe('#getHeader', function() { describe('#getHeader', function() {
it('should be compatible with simple proposal legacy header', function() {
var x = TxProposal.fromObj(aTXP());
var proposalHeader = x.getHeader();
var pH = WalletUtils.getProposalHash.apply(WalletUtils, proposalHeader);
var uH = WalletUtils.getProposalHash(x.toAddress, x.amount, x.message, x.payProUrl);
pH.should.equal(uH);
});
it('should handle multiple-outputs', function() { it('should handle multiple-outputs', function() {
var x = TxProposal.fromObj(aTXP(TxProposal.Types.MULTIPLEOUTPUTS)); var x = TxProposal.fromObj(aTXP(TxProposal.Types.MULTIPLEOUTPUTS));
var proposalHeader = x.getHeader(); var proposalHeader = x.getHeader();
should.exist(proposalHeader); should.exist(proposalHeader);
var pH = WalletUtils.getProposalHash.apply(WalletUtils, proposalHeader); var pH = WalletUtils.getProposalHash(proposalHeader);
should.exist(pH); should.exist(pH);
}); });
}); });

Loading…
Cancel
Save