checking arguments

This commit is contained in:
Ivan Socolsky 2015-02-02 15:29:14 -03:00
parent 0885b26983
commit 07012633a9
6 changed files with 87 additions and 21 deletions

View File

@ -10,17 +10,17 @@ var MESSAGE_SIGNING_PATH = "m/1/0";
function Copayer(opts) { function Copayer(opts) {
opts = opts || {}; opts = opts || {};
this.version = VERSION;
this.createdOn = Math.floor(Date.now() / 1000); this.createdOn = Math.floor(Date.now() / 1000);
this.id = opts.id; this.id = opts.id;
this.name = opts.name; this.name = opts.name;
this.xPubKey = opts.xPubKey; this.xPubKey = opts.xPubKey;
this.xPubKeySignature = opts.xPubKeySignature; // So third parties can check independently this.xPubKeySignature = opts.xPubKeySignature; // So third parties can check independently
this.version = VERSION; if (opts.xPubKey) {
this.signingPubKey = opts.signingPubKey || this.getSigningPubKey(); this.signingPubKey = this.getSigningPubKey();
}
}; };
Copayer.prototype.getSigningPubKey = function () { Copayer.prototype.getSigningPubKey = function () {
if (!this.xPubKey) return null; if (!this.xPubKey) return null;
return HDPublicKey.fromString(this.xPubKey).derive(MESSAGE_SIGNING_PATH).publicKey.toString(); return HDPublicKey.fromString(this.xPubKey).derive(MESSAGE_SIGNING_PATH).publicKey.toString();
@ -29,13 +29,13 @@ Copayer.prototype.getSigningPubKey = function () {
Copayer.fromObj = function (obj) { Copayer.fromObj = function (obj) {
var x = new Copayer(); var x = new Copayer();
x.version = obj.version;
x.createdOn = obj.createdOn; x.createdOn = obj.createdOn;
x.id = obj.id; x.id = obj.id;
x.name = obj.name; x.name = obj.name;
x.xPubKey = obj.xPubKey; x.xPubKey = obj.xPubKey;
x.xPubKeySignature = obj.xPubKeySignature; x.xPubKeySignature = obj.xPubKeySignature;
x.signingPubKey = obj.signingPubKey || this.getSigningPubKey(); x.signingPubKey = obj.signingPubKey;
return x; return x;
}; };

View File

@ -4,9 +4,12 @@ var _ = require('lodash');
var TxProposalAction = require('./txproposalaction'); var TxProposalAction = require('./txproposalaction');
var VERSION = '1.0.0';
function TxProposal(opts) { function TxProposal(opts) {
opts = opts || {}; opts = opts || {};
this.version = VERSION;
this.createdOn = Math.floor(Date.now() / 1000); this.createdOn = Math.floor(Date.now() / 1000);
this.id = opts.id; this.id = opts.id;
this.creatorId = opts.creatorId; this.creatorId = opts.creatorId;
@ -24,6 +27,7 @@ function TxProposal(opts) {
TxProposal.fromObj = function (obj) { TxProposal.fromObj = function (obj) {
var x = new TxProposal(); var x = new TxProposal();
x.version = obj.version;
x.createdOn = obj.createdOn; x.createdOn = obj.createdOn;
x.id = obj.id; x.id = obj.id;
x.creatorId = obj.creatorId; x.creatorId = obj.creatorId;

View File

@ -3,11 +3,12 @@
var _ = require('lodash'); var _ = require('lodash');
var Copayer = require('./copayer'); var Copayer = require('./copayer');
var WALLET_VERSION = '1.0.0'; var VERSION = '1.0.0';
function Wallet(opts) { function Wallet(opts) {
opts = opts || {}; opts = opts || {};
this.version = VERSION;
this.createdOn = Math.floor(Date.now() / 1000); this.createdOn = Math.floor(Date.now() / 1000);
this.id = opts.id; this.id = opts.id;
this.name = opts.name; this.name = opts.name;
@ -17,23 +18,43 @@ function Wallet(opts) {
this.publicKeyRing = []; this.publicKeyRing = [];
this.addressIndex = 0; this.addressIndex = 0;
this.copayers = []; this.copayers = [];
this.version = WALLET_VERSION;
this.pubKey = opts.pubKey; this.pubKey = opts.pubKey;
}; };
/* For compressed keys, m*73 + n*34 <= 496 */
Wallet.COPAYER_PAIR_LIMITS = {
1: 1,
2: 2,
3: 3,
4: 4,
5: 4,
6: 4,
7: 3,
8: 3,
9: 2,
10: 2,
11: 1,
12: 1,
};
Wallet.fromUntrustedObj = function (obj) { /**
* Get the maximum allowed number of required copayers.
* This is a limit imposed by the maximum allowed size of the scriptSig.
* @param {number} totalCopayers - the total number of copayers
* @return {number}
*/
Wallet.getMaxRequiredCopayers = function(totalCopayers) {
return Wallet.COPAYER_PAIR_LIMITS[totalCopayers];
};
// TODO add sanity checks OR migration steps? Wallet.verifyCopayerLimits = function (m, n) {
if (!obj.pubKey || !obj.m || !obj.n) return (n >= 1 && n <= 12) && (m >= 1 && m <= Wallet.COPAYER_PAIR_LIMITS[n]);
return cb('Wallet corrupted');
return Wallet.fromObj(obj);
}; };
Wallet.fromObj = function (obj) { Wallet.fromObj = function (obj) {
var x = new Wallet(); var x = new Wallet();
x.version = obj.version;
x.createdOn = obj.createdOn; x.createdOn = obj.createdOn;
x.id = obj.id; x.id = obj.id;
x.name = obj.name; x.name = obj.name;

View File

@ -5,11 +5,13 @@ var $ = require('preconditions').singleton();
var async = require('async'); var async = require('async');
var log = require('npmlog'); var log = require('npmlog');
log.debug = log.verbose; log.debug = log.verbose;
var Bitcore = require('bitcore'); var Bitcore = require('bitcore');
var PublicKey = Bitcore.PublicKey; var PublicKey = Bitcore.PublicKey;
var HDPublicKey = Bitcore.HDPublicKey; var HDPublicKey = Bitcore.HDPublicKey;
var Explorers = require('bitcore-explorers'); var Explorers = require('bitcore-explorers');
var utils = require('./utils');
var Lock = require('./lock'); var Lock = require('./lock');
var Storage = require('./storage'); var Storage = require('./storage');
var SignUtils = require('./signutils'); var SignUtils = require('./signutils');
@ -45,6 +47,11 @@ function CopayServer(opts) {
CopayServer.prototype.createWallet = function (opts, cb) { CopayServer.prototype.createWallet = function (opts, cb) {
var self = this, pubKey; var self = this, pubKey;
utils.checkRequired(opts, ['id', 'name', 'm', 'n', 'pubKey']);
if (!Wallet.verifyCopayerLimits(opts.m, opts.n)) return cb('Incorrect m or n value');
var network = opts.network || 'livenet';
if (network != 'livenet' && network != 'testnet') return cb('Invalid network');
try { try {
pubKey = new PublicKey.fromString(opts.pubKey); pubKey = new PublicKey.fromString(opts.pubKey);
} catch (e) { } catch (e) {
@ -60,7 +67,7 @@ CopayServer.prototype.createWallet = function (opts, cb) {
name: opts.name, name: opts.name,
m: opts.m, m: opts.m,
n: opts.n, n: opts.n,
network: opts.network || 'livenet', network: network,
pubKey: pubKey, pubKey: pubKey,
}); });
@ -119,6 +126,8 @@ CopayServer.prototype._verifySignature = function (text, signature, pubKey) {
CopayServer.prototype.joinWallet = function (opts, cb) { CopayServer.prototype.joinWallet = function (opts, cb) {
var self = this; var self = this;
utils.checkRequired(opts, ['walletId', 'id', 'name', 'xPubKey', 'xPubKeySignature']);
self._runLocked(opts.walletId, cb, function (cb) { self._runLocked(opts.walletId, cb, function (cb) {
self.getWallet({ id: opts.walletId }, function (err, wallet) { self.getWallet({ id: opts.walletId }, function (err, wallet) {
if (err) return cb(err); if (err) return cb(err);
@ -164,6 +173,8 @@ CopayServer.prototype._doCreateAddress = function (pkr, index, isChange) {
CopayServer.prototype.createAddress = function (opts, cb) { CopayServer.prototype.createAddress = function (opts, cb) {
var self = this; var self = this;
utils.checkRequired(opts, ['walletId', 'isChange']);
self._runLocked(opts.walletId, cb, function (cb) { self._runLocked(opts.walletId, cb, function (cb) {
self.getWallet({ id: opts.walletId }, function (err, wallet) { self.getWallet({ id: opts.walletId }, function (err, wallet) {
if (err) return cb(err); if (err) return cb(err);
@ -195,6 +206,8 @@ CopayServer.prototype._doCreateAddress = function (pkr, index, isChange) {
CopayServer.prototype.verifyMessageSignature = function (opts, cb) { CopayServer.prototype.verifyMessageSignature = function (opts, cb) {
var self = this; var self = this;
utils.checkRequired(opts, ['walletId', 'copayerId', 'message', 'signature']);
self.getWallet({ id: opts.walletId }, function (err, wallet) { self.getWallet({ id: opts.walletId }, function (err, wallet) {
if (err) return cb(err); if (err) return cb(err);
@ -275,6 +288,8 @@ CopayServer.prototype._getUtxos = function (opts, cb) {
CopayServer.prototype.getBalance = function (opts, cb) { CopayServer.prototype.getBalance = function (opts, cb) {
var self = this; var self = this;
utils.checkRequired(opts, 'walletId');
self._getUtxos({ walletId: opts.walletId }, function (err, utxos) { self._getUtxos({ walletId: opts.walletId }, function (err, utxos) {
if (err) return cb(err); if (err) return cb(err);
@ -326,6 +341,8 @@ CopayServer.prototype._selectUtxos = function (txp, utxos) {
CopayServer.prototype.createTx = function (opts, cb) { CopayServer.prototype.createTx = function (opts, cb) {
var self = this; var self = this;
utils.checkRequired(opts, ['walletId', 'copayerId', 'toAddress', 'amount', 'message']);
self.getWallet({ id: opts.walletId }, function (err, wallet) { self.getWallet({ id: opts.walletId }, function (err, wallet) {
if (err) return cb(err); if (err) return cb(err);
@ -373,6 +390,8 @@ CopayServer.prototype._broadcastTx = function (rawTx, cb) {
CopayServer.prototype.signTx = function (opts, cb) { CopayServer.prototype.signTx = function (opts, cb) {
var self = this; var self = this;
utils.checkRequired(opts, ['walletId', 'copayerId', 'txProposalId', 'signature']);
self.fetchTx(opts.walletId, opts.txProposalId, function (err, txp) { self.fetchTx(opts.walletId, opts.txProposalId, function (err, txp) {
if (err) return cb(err); if (err) return cb(err);
if (!txp) return cb('Transaction proposal not found'); if (!txp) return cb('Transaction proposal not found');
@ -406,10 +425,13 @@ CopayServer.prototype.signTx = function (opts, cb) {
* @param {string} opts.walletId - The wallet id. * @param {string} opts.walletId - The wallet id.
* @param {string} opts.copayerId - The wallet id. * @param {string} opts.copayerId - The wallet id.
* @param {string} opts.txProposalId - The identifier of the transaction. * @param {string} opts.txProposalId - The identifier of the transaction.
* @param {string} [opts.reason] - A message to other copayers explaining the rejection.
*/ */
CopayServer.prototype.rejectTx = function (opts, cb) { CopayServer.prototype.rejectTx = function (opts, cb) {
var self = this; var self = this;
utils.checkRequired(opts, ['walletId', 'copayerId', 'txProposalId']);
self.fetchTx(opts.walletId, opts.txProposalId, function (err, txp) { self.fetchTx(opts.walletId, opts.txProposalId, function (err, txp) {
if (err) return cb(err); if (err) return cb(err);
if (!txp) return cb('Transaction proposal not found'); if (!txp) return cb('Transaction proposal not found');
@ -436,6 +458,8 @@ CopayServer.prototype.rejectTx = function (opts, cb) {
CopayServer.prototype.getPendingTxs = function (opts, cb) { CopayServer.prototype.getPendingTxs = function (opts, cb) {
var self = this; var self = this;
utils.checkRequired(opts, 'walletId');
self.storage.fetchTxs(opts.walletId, function (err, txps) { self.storage.fetchTxs(opts.walletId, function (err, txps) {
if (err) return cb(err); if (err) return cb(err);

14
lib/utils.js Normal file
View File

@ -0,0 +1,14 @@
var $ = require('preconditions').singleton();
var _ = require('lodash');
var utils = {};
utils.checkRequired = function (obj, args) {
args = [].concat(args);
if (!_.isObject(obj)) throw 'Required arguments missing';
_.each(args, function (arg) {
if (!obj.hasOwnProperty(arg)) throw "Missing required argument '" + arg + "'";
});
};
module.exports = utils;

View File

@ -428,10 +428,12 @@ describe('Copay server', function() {
name: 'me', name: 'me',
xPubKey: someXPubKeys[0], xPubKey: someXPubKeys[0],
}; };
server.joinWallet(copayerOpts, function(err) { try {
err.should.contain('Bad request'); server.joinWallet(copayerOpts, function(err) {});
} catch (e) {
e.should.contain('xPubKeySignature');
done(); done();
}); }
}); });
}); });
@ -526,11 +528,12 @@ describe('Copay server', function() {
it('should create address', function(done) { it('should create address', function(done) {
server._doCreateAddress = sinon.stub().returns(new Address({ server._doCreateAddress = sinon.stub().returns(new Address({
address: 'addr1', address: 'addr1',
path: 'path1' path: 'path1',
})); }));
helpers.createAndJoinWallet('123', 2, 2, function(err, wallet) { helpers.createAndJoinWallet('123', 2, 2, function(err, wallet) {
server.createAddress({ server.createAddress({
walletId: '123' walletId: '123',
isChange: false,
}, function(err, address) { }, function(err, address) {
should.not.exist(err); should.not.exist(err);
address.should.exist; address.should.exist;