From 53be45ac4cd652a85d16bb4cfb46512e345ca921 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Tue, 17 Feb 2015 20:26:58 -0300 Subject: [PATCH 1/4] refactor copayer construction --- lib/client/api.js | 10 ++++---- lib/model/copayer.js | 55 ++++++++++++++++++++++++++------------------ lib/server.js | 2 +- lib/walletutils.js | 2 +- test/testdata.js | 10 ++++++++ 5 files changed, 49 insertions(+), 30 deletions(-) diff --git a/lib/client/api.js b/lib/client/api.js index caafc49..c1750f9 100644 --- a/lib/client/api.js +++ b/lib/client/api.js @@ -346,10 +346,10 @@ API.prototype.import = function(str, cb) { var xPubKey = (new Bitcore.HDPublicKey(data.xPrivKey)).toString(); data.publicKeyRing.push(xPubKey); - data.copayerId = WalletUtils.xpubToCopayerId(xPubKey); + data.copayerId = WalletUtils.xPubToCopayerId(xPubKey); data.m = data.publicKeyRing.length; data.signingPrivKey = (new Bitcore.HDPrivateKey(data.xPrivKey)).derive('m/1/0').privateKey.toWIF(); - data.network = data.xPrivKey.substr(0,4) === 'tprv' ? 'testnet' : 'livenet'; + data.network = data.xPrivKey.substr(0, 4) === 'tprv' ? 'testnet' : 'livenet'; self.storage.save(data, cb); }); }; @@ -371,9 +371,9 @@ API.prototype.signTxProposal = function(txp, cb) { this._loadAndCheck(function(err, data) { if (err) return cb(err); - if (!Verifier.checkTxProposal(data, txp)) { - return cb(new ServerCompromisedError('Server sent fake transaction proposal')); - } + if (!Verifier.checkTxProposal(data, txp)) { + return cb(new ServerCompromisedError('Server sent fake transaction proposal')); + } //Derive proper key to sign, for each input diff --git a/lib/model/copayer.js b/lib/model/copayer.js index b9bfea1..4e9ce77 100644 --- a/lib/model/copayer.js +++ b/lib/model/copayer.js @@ -14,46 +14,55 @@ var Utils = require('../walletutils'); var VERSION = '1.0.0'; var MESSAGE_SIGNING_PATH = "m/1/0"; -function Copayer(opts) { +function Copayer() { + this.version = VERSION; +}; + +Copayer.create = function(opts) { $.checkArgument(opts && opts.xPubKey, 'need to provide an xPubKey'); + opts.copayerIndex = opts.copayerIndex || 0; - this.xPubKey = opts.xPubKey; - this.id = Utils.xpubToCopayerId(this.xPubKey); + var x = new Copayer(); + x.createdOn = Math.floor(Date.now() / 1000); - this.version = VERSION; - this.createdOn = Math.floor(Date.now() / 1000); - this.name = opts.name; - this.xPubKeySignature = opts.xPubKeySignature; // So third parties can check independently - if (this.xPubKey) - this.signingPubKey = this.getSigningPubKey(); - this.addressManager = new AddressManager({ copayerIndex: opts.copayerIndex }); -}; + x.xPubKey = opts.xPubKey; -Copayer.prototype.getPublicKey = function(path) { - return HDPublicKey - .fromString(this.xPubKey) - .derive(path) - .publicKey - .toString(); -}; + x.id = Utils.xPubToCopayerId(x.xPubKey); + x.name = opts.name; + x.xPubKeySignature = opts.xPubKeySignature; // So third parties can check independently + x.signingPubKey = x.getSigningPubKey(); + x.addressManager = new AddressManager({ + copayerIndex: opts.copayerIndex + }); -Copayer.prototype.getSigningPubKey = function() { - return this.getPublicKey(MESSAGE_SIGNING_PATH); + return x; }; Copayer.fromObj = function(obj) { - var x = new Copayer({ - xPubKey: obj.xPubKey, - }); + var x = new Copayer(); x.createdOn = obj.createdOn; x.id = obj.id; x.name = obj.name; + x.xPubKey = obj.xPubKey; x.xPubKeySignature = obj.xPubKeySignature; + x.signingPubKey = obj.signingPubKey; x.addressManager = AddressManager.fromObj(obj.addressManager); return x; }; +Copayer.prototype.getPublicKey = function(path) { + return HDPublicKey + .fromString(this.xPubKey) + .derive(path) + .publicKey + .toString(); +}; + +Copayer.prototype.getSigningPubKey = function() { + return this.getPublicKey(MESSAGE_SIGNING_PATH); +}; + module.exports = Copayer; diff --git a/lib/server.js b/lib/server.js index 7b76300..38d4a23 100644 --- a/lib/server.js +++ b/lib/server.js @@ -212,7 +212,7 @@ CopayServer.prototype.joinWallet = function(opts, cb) { if (wallet.copayers.length == wallet.n) return cb(new ClientError('WFULL', 'Wallet full')); - var copayer = new Copayer({ + var copayer = Copayer.create({ name: opts.name, xPubKey: opts.xPubKey, xPubKeySignature: opts.xPubKeySignature, diff --git a/lib/walletutils.js b/lib/walletutils.js index 9e325fa..f603f33 100644 --- a/lib/walletutils.js +++ b/lib/walletutils.js @@ -57,7 +57,7 @@ WalletUtils.getProposalHash = function(toAddress, amount, message) { return toAddress + '|' + amount + '|' + (message || ''); }; -WalletUtils.xpubToCopayerId = function(xpub) { +WalletUtils.xPubToCopayerId = function(xpub) { return (new Bitcore.HDPublicKey(xpub)).derive(HDPath.IdBranch).publicKey.toString(); }; diff --git a/test/testdata.js b/test/testdata.js index 6bc36b1..0de3304 100644 --- a/test/testdata.js +++ b/test/testdata.js @@ -9,60 +9,70 @@ var message = { }; var copayers = [{ + id: '03bc5f0504c8dd480926dbe90449ccc7b2fc4c96e79f1cb8ffcd31731e2ee8db9b', xPrivKey: 'xprv9s21ZrQH143K2rMHbXTJmWTuFx6ssqn1vyRoZqPkCXYchBSkp5ey8kMJe84sxfXq5uChWH4gk94rWbXZt2opN9kg4ufKGvUM7HQSLjnoh7e', xPubKey: 'xpub661MyMwAqRbcFLRkhYzK8eQdoywNHJVsJCMQNDoMks5bZymuMcyDgYfnVQYq2Q9npnVmdTAthYGc3N3uxm5sEdnTpSqBc4YYTAhNnoSxCm9', xPubKeySignature: '30440220192ae7345d980f45f908bd63ccad60ce04270d07b91f1a9d92424a07a38af85202201591f0f71dd4e79d9206d2306862e6b8375e13a62c193953d768e884b6fb5a46', // signed using keyPair.priv privKey: '5c0e043a513032907d181325a8e7990b076c0af15ed13dc5e611cda9bb3ae52a', // derived with 'm/1/0' pubKey: '03814ac7decf64321a3c6967bfb746112fdb5b583531cd512cc3787eaf578947dc', }, { + id: '0235223413f8219260aa892c518969701af7c339284afecc7044f98dd47d48754b', xPrivKey: 'xprv9s21ZrQH143K2JgXh8Va3Taq22D2gXw2nYULffV5dc9acQvAmB3KhomPKGwV9AbVsBcAXMW2QxCnvmHU1rVtHRfZwTxdEEAN5ZojRYdryQ1', xPubKey: 'xpub661MyMwAqRbcEnkzoA2aQbXZa43X5zet9mPwU3thBwgZVDFKJiMaFc5sAYS97qVtMxpvceittobtoH2JKmpweSN1CLSe91hiE1Wrf5YJhsQ', xPubKeySignature: '3045022100b9079d0d9b70da828b0e9c776fe01c5c13fc254a314c0b09a638c8695ec9360c022079922950779080569163c692ed8ee882f9ef37e7b2dff03de9c6756e7599960e', privKey: '7708f0b7a60da9b88893e41eb2f59acf13ddc38edd794e1af9c2b57d35e99d85', pubKey: '0266efb3b02973233636b153296486cdbc1728e1f42a1062f030af193ab14e1321', }, { + id: '023f31628856799ce6a12d02d362fbf6acc2c112207eeeb7bd34a048ec2127645d', xPrivKey: 'xprv9s21ZrQH143K2DoxHNrecLmp121HU4nZRB57jj2cGmSkp9Wrgz2AevFT98AcYocYXEyyWDwC1JUn13beDjQU87FwfCaHiWhgoSx9G31tmDa', xPubKey: 'xpub661MyMwAqRbcEhtRPQPeyUiYZ3qmsXWQnPziY7SDq6yjgwr1EXLRCiZvzQsfnQehH8hBzCxNPYCJXx51QKcbervpkBK931H1A9F39z5E1XD', xPubKeySignature: '3045022100ed04aca131acf6f030018a7e3dd564788bbad5528e9edb7880f032ae6917bf10022022879ca8a60700c9c3bf9d603ae2555d4be1914084c912579b4bc09a432e9c32', privKey: 'a8e36ab9e065a5cc0551938c225d40542b7780a84aff55cb52e4eb4d7f64de11', pubKey: '033e44b94789a591380787effbad0a01f41aedd7ed315c931efc51284e99ae34a9', }, { + id: '024062aae3d6575b03a8532e618feeb70d6926c069e24cca0cb1a24db4dec6562e', xPrivKey: 'xprv9s21ZrQH143K3dHn6j7zuLSnnMpBAceHfQFDm6R3wbkD26pTSUuZ7JY4549T8mMhwwUeq6SW6guDwy6cUhqs6PwoF2svKKkLjJdeKi1BzUn', xPubKey: 'xpub661MyMwAqRbcG7NFCkf1GUPXLPefa5N92dApZUpfVwHBtu9bz2Dof6rXvKjioVpLXRBHKeyX2AxLdtaCbUtz2zx3dE9F4mPkDsARrPaGsSL', xPubKeySignature: '3045022100a17dee46810e379aa37104ec1a4e20276aa41eac67b7e555475b15db6f6ee8ca0220472d114368f6d78bd8dba5c5fed77b22437341621d7879331bc48f7ee7701853', privKey: '68cc2c1776f3456fcdef12812e634b6adbd676c5b8168a4b2547cf73c5363cfd', pubKey: '027597e3a18c829dfdd92f875ae87f2aa4654cbb13db28a92a19401cf6f8ac18cb', }, { + id: '031114b2afe5d1e87d834ac71798028724f8f89e2d3324b5e6abb513fdcde2e69d', xPrivKey: 'xprv9s21ZrQH143K3Wac3NKJ8nZsBsn4HboPtCD2LDB8zkXcmo9q97efAi9i6KkcyBoJ4vjD59corGsdJebmNXud4nH3bCBSo64uWfwauo3Kdco', xPubKey: 'xpub661MyMwAqRbcFzf59PrJVvWbjucYh4XFFR8d8bakZ64bebUygexuiWUBwa7EnFosoFstoocLQTrLPeAQThonSrYTDQ18gkS219dLJuwUHDb', xPubKeySignature: '30440220023c1902434aaca0c2ed3c92262d56ee4296cd5c7598b009b8556deab2df89e70220714117970debf5cc1232441aa2d1ce335b5b99958acb2d000c4241e2a2fc567f', privKey: 'fc97d94e97857b2016a1e68ab0313ce2ea5a791638e8eb0bb6d5a5aa72de01df', pubKey: '03a3e04a3fb218b074306ca2a4995ec1ea97ef2b73dd54edfbae9ee56651dd21cc', }, { + id: '03d5352705f1f587dad9814cbdae1ec627a9252f18ca677cd9d5243d41d44a7fea', xPrivKey: 'xprv9s21ZrQH143K2Hxh1Xj59WjAimbDuubNBFknLYPr563BBziVuLhMvw7F3CkYMYj1y6QSbDnKQVHrMGekXi7awjLwKR1XWcMoR3eKEcH65Pa', xPubKey: 'xpub661MyMwAqRbcEn3A7ZG5WefuGoRiKNKDYUgP8voTdRaA4o3eSt1cUjRitVc75a2gsifRDufbicYXeQCchDvkRKSSTWi1uy7PPaNHnerGvTa', xPubKeySignature: '3044022042f063cd154a359f1d49202d79efc0737c47077ee50e36ed3d796327b9b29a9602206a4baf2902b49d1cb265eb31b9ab12d470fd023a11d9d425679a1630ba0b0e29', privKey: '72381546174e6165e39853510bf3353645cb2e19e0499d3bb1d8aef0027352eb', pubKey: '037e4163f69c3e2b05e980e3ac5dac06730894b1ba520a15a9d8221245d4bfe2a4', }, { + id: '036fa483c6f226afecb2782a69ab11d1cf43c26f277280cb2ec69f88681d8a4418', xPrivKey: 'xprv9s21ZrQH143K2L2wxVQ5nJ8FrTjz2KHB4wy83Xu6y6jdxmzrKNWvh6g7apPJqwj5NjhrmyJ6TYe9Hk4fbYx4tRg7Zk4Y7dAdgej4RVeUBTn', xPubKey: 'xpub661MyMwAqRbcEp7R4Ww69S4zQVaURn12SAtiqvJiXSGcqaKzruqBEtzbS97V145KiYxsW4g9M3pqsibfc5mtbMn4R52v7bnnrHGAoiHb1pz', xPubKeySignature: '304402207b082a63fc39b90a0f18edeb20d191430f11a9e5378681f15a68aac052aa858202201fd127b374e4a301a45f0c73d2a747d156a60075e643308e489a672bd0a7b4fb', privKey: 'daff48eab1268c23e9ecde993b93ace4a375e7c627da0d3662746c7e3fecbdfa', pubKey: '02035c09deeef7436df39a09ef2167129c686c6177185216525b9d778af96bacd7', }, { + id: '03bc5f0504c8dd480926dbe90449ccc7b2fc4c96e79f1cb8ffcd31731e2ee8db9b', xPrivKey: 'xprv9s21ZrQH143K3Pqe7LhTkE84VM6GysvSvggfhS3KbHvgBLaaQeR9YNePRo3vpLMVBv5SNhNVAaEDyj6Q8vMRVYM4X9bWYCiPgsJXkH8WzX1', xPubKey: 'xpub661MyMwAqRbcFLRkhYzK8eQdoywNHJVsJCMQNDoMks5bZymuMcyDgYfnVQYq2Q9npnVmdTAthYGc3N3uxm5sEdnTpSqBc4YYTAhNnoSxCm9', xPubKeySignature: '30440220192ae7345d980f45f908bd63ccad60ce04270d07b91f1a9d92424a07a38af85202201591f0f71dd4e79d9206d2306862e6b8375e13a62c193953d768e884b6fb5a46', privKey: '5c0e043a513032907d181325a8e7990b076c0af15ed13dc5e611cda9bb3ae52a', pubKey: '03814ac7decf64321a3c6967bfb746112fdb5b583531cd512cc3787eaf578947dc', }, { + id: '02ec9ec4fd013c67312e0365128d470a89e13e53d9b44f026c5eca0b6e2ab9ae29', xPrivKey: 'xprv9s21ZrQH143K2QSAHGxQhUsJYFDcZ6h2oiTjKFPmbnzeNzXgRW73NwX7ifBgbJ35eHGR7toyj9CCXB6Wzf5iCjj3YDuJuvBoJFJsiQAdTUH', xPubKey: 'xpub661MyMwAqRbcEtWdPJVR4cp36H46xZQtAwPL7doPA8XdFnrpy3RHvjqbZxERYNMd4E2tt84xy4F2PqtKkHFDzZbSAaUabp36oZDwwPEqFjK', xPubKeySignature: '304502210081a88684d4e27cab752d0df6a746aeb4bbcac57e73edd3847ffb43f1cf6740b20220312598f47dc5e775ea2ba97048764675afa4796173115049b4dacc8882b5c7b7', privKey: '01273975489d85ac06f2e47677149420cd4901264cb40e3a756ff901acfc11f6', pubKey: '0210eed257f41c9a991f8bd9523f66c3a83c1aab27cd2ccf233f8f5c3caef77e7a', }, { + id: '02ec5f9178f77b306bc92362f3c1ef8f10b8cce44dc255ba20435f24d6459981db', xPrivKey: 'xprv9s21ZrQH143K2bj7Azs1rCkumDJmbNveDA96wDJThzsDEJjBngkFXEr646AbvrTAfRd2scqq7hN48fGXesobx4sKRkddCrLaCpoWUkMJErj', xPubKey: 'xpub661MyMwAqRbcF5oaH2Q2DLheKF9FzqeVaP4hjbi5GLQC774LLE4W53AZuMztQ6e6SMmEMj8K8zsP3iMMnJgK2PawWZCh7QcdgAg7eJWSJFr', xPubKeySignature: '304402207781231f8bd9a679938057373702afdeec43e84b5b239e2e4dc8e35c63e4ee7102207f7ed929c81dfb59ebd14f56609dcd8255de6337c967704340a2089080fd896f', From 5c765600b64987e3170b94daddf376b9882bfe05 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Tue, 17 Feb 2015 20:54:59 -0300 Subject: [PATCH 2/4] use getProposalHash in client --- lib/client/api.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/client/api.js b/lib/client/api.js index c1750f9..e9f7ac7 100644 --- a/lib/client/api.js +++ b/lib/client/api.js @@ -18,8 +18,8 @@ var BASE_URL = 'http://localhost:3001/copay/api'; var WALLET_CRITICAL_DATA = ['xPrivKey', 'm', 'publicKeyRing']; function _createProposalOpts(opts, signingKey) { - var msg = opts.toAddress + '|' + opts.amount + '|' + opts.message; - opts.proposalSignature = WalletUtils.signMessage(msg, signingKey); + var hash = WalletUtils.getProposalHash(opts.toAddress, opts.amount, opts.message); + opts.proposalSignature = WalletUtils.signMessage(hash, signingKey); return opts; }; From 4c7f8d3a53f739abdfc354177986f024a11f2bb4 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Tue, 17 Feb 2015 20:58:04 -0300 Subject: [PATCH 3/4] refactor Wallet object creation --- lib/model/wallet.js | 76 +++++++++++++++++++++++++-------------------- lib/server.js | 2 +- 2 files changed, 43 insertions(+), 35 deletions(-) diff --git a/lib/model/wallet.js b/lib/model/wallet.js index 84c9cfa..634312a 100644 --- a/lib/model/wallet.js +++ b/lib/model/wallet.js @@ -12,22 +12,50 @@ var WalletUtils = require('../walletutils'); var VERSION = '1.0.0'; -function Wallet(opts) { - opts = opts || {}; +function Wallet() { this.version = VERSION; - this.createdOn = Math.floor(Date.now() / 1000); - this.id = Uuid.v4(); - this.name = opts.name; - this.m = opts.m; - this.n = opts.n; - this.status = 'pending'; - this.publicKeyRing = []; - this.addressIndex = 0; - this.copayers = []; - this.pubKey = opts.pubKey; - this.network = opts.network; - this.addressManager = new AddressManager(); +}; + +Wallet.create = function(opts) { + opts = opts || {}; + + var x = new Wallet(); + + x.createdOn = Math.floor(Date.now() / 1000); + x.id = Uuid.v4(); + x.name = opts.name; + x.m = opts.m; + x.n = opts.n; + x.status = 'pending'; + x.publicKeyRing = []; + x.addressIndex = 0; + x.copayers = []; + x.pubKey = opts.pubKey; + x.network = opts.network; + x.addressManager = new AddressManager(); + + return x; +}; + +Wallet.fromObj = function(obj) { + var x = new Wallet(); + + x.createdOn = obj.createdOn; + x.id = obj.id; + x.name = obj.name; + x.m = obj.m; + x.n = obj.n; + x.status = obj.status; + x.publicKeyRing = obj.publicKeyRing; + x.copayers = _.map(obj.copayers, function(copayer) { + return Copayer.fromObj(copayer); + }); + x.pubKey = obj.pubKey; + x.network = obj.network; + x.addressManager = AddressManager.fromObj(obj.addressManager); + + return x; }; /* For compressed keys, m*73 + n*34 <= 496 */ @@ -60,26 +88,6 @@ Wallet.verifyCopayerLimits = function(m, n) { return (n >= 1 && n <= 12) && (m >= 1 && m <= Wallet.COPAYER_PAIR_LIMITS[n]); }; -Wallet.fromObj = function(obj) { - var x = new Wallet(); - - x.createdOn = obj.createdOn; - x.id = obj.id; - x.name = obj.name; - x.m = obj.m; - x.n = obj.n; - x.status = obj.status; - x.publicKeyRing = obj.publicKeyRing; - x.copayers = _.map(obj.copayers, function(copayer) { - return Copayer.fromObj(copayer); - }); - x.pubKey = obj.pubKey; - x.network = obj.network; - x.addressManager = AddressManager.fromObj(obj.addressManager); - - return x; -}; - Wallet.prototype.isShared = function() { return this.n > 1; }; diff --git a/lib/server.js b/lib/server.js index 38d4a23..18d61f1 100644 --- a/lib/server.js +++ b/lib/server.js @@ -114,7 +114,7 @@ CopayServer.prototype.createWallet = function(opts, cb) { return cb(e.toString()); }; - var wallet = new Wallet({ + var wallet = Wallet.create({ name: opts.name, m: opts.m, n: opts.n, From 52f6529952d23f26442d2e3c157ebfba21d19cd8 Mon Sep 17 00:00:00 2001 From: Ivan Socolsky Date: Tue, 17 Feb 2015 21:20:08 -0300 Subject: [PATCH 4/4] refactor remaining objects --- lib/model/address.js | 21 ++++++++++++------ lib/model/addressmanager.js | 30 ++++++++++++++++--------- lib/model/copayer.js | 5 ++--- lib/model/notification.js | 26 +++++++++++++--------- lib/model/txproposal.js | 42 +++++++++++++++++++---------------- lib/model/txproposalaction.js | 22 ++++++++++++------ lib/model/wallet.js | 9 +++----- lib/server.js | 4 ++-- test/addressmanager.js | 6 ++--- 9 files changed, 98 insertions(+), 67 deletions(-) diff --git a/lib/model/address.js b/lib/model/address.js index ef042d0..c8ffa38 100644 --- a/lib/model/address.js +++ b/lib/model/address.js @@ -2,16 +2,23 @@ var Bitcore = require('bitcore'); -function Address(opts) { +function Address() { + this.version = '1.0.0'; +}; + +Address.create = function(opts) { opts = opts || {}; - this.createdOn = Math.floor(Date.now() / 1000); - this.address = opts.address; - this.path = opts.path; - this.publicKeys = opts.publicKeys; + var x = new Address(); + + x.createdOn = Math.floor(Date.now() / 1000); + x.address = opts.address; + x.path = opts.path; + x.publicKeys = opts.publicKeys; + return x; }; -Address.fromObj = function (obj) { +Address.fromObj = function(obj) { var x = new Address(); x.createdOn = obj.createdOn; @@ -28,7 +35,7 @@ Address.fromObj = function (obj) { * @param {number} threshold - amount of required signatures to spend the output * @return {Script} */ -Address.prototype.getScriptPubKey = function (threshold) { +Address.prototype.getScriptPubKey = function(threshold) { return Bitcore.Script.buildMultisigOut(this.publicKeys, threshold).toScriptHashOut(); }; diff --git a/lib/model/addressmanager.js b/lib/model/addressmanager.js index 6786016..a2e6305 100644 --- a/lib/model/addressmanager.js +++ b/lib/model/addressmanager.js @@ -1,24 +1,34 @@ var _ = require('lodash'); var HDPath = require('../hdpath'); -function AddressManager(opts) { - this.receiveAddressIndex = 0; - this.changeAddressIndex = 0; - this.copayerIndex = (opts && _.isNumber(opts.copayerIndex)) ? opts.copayerIndex : HDPath.SHARED_INDEX; +function AddressManager() { + this.version = '1.0.0'; }; +AddressManager.create = function(opts) { + opts = opts || {}; -AddressManager.fromObj = function (obj) { var x = new AddressManager(); - x.receiveAddressIndex = obj.receiveAddressIndex; + x.receiveAddressIndex = 0; + x.changeAddressIndex = 0; + x.copayerIndex = (opts && _.isNumber(opts.copayerIndex)) ? opts.copayerIndex : HDPath.SHARED_INDEX; + + return x; +}; + + +AddressManager.fromObj = function(obj) { + var x = new AddressManager(); + + x.receiveAddressIndex = obj.receiveAddressIndex; x.changeAddressIndex = obj.changeAddressIndex; x.copayerIndex = obj.copayerIndex; return x; }; -AddressManager.prototype._incrementIndex = function (isChange) { +AddressManager.prototype._incrementIndex = function(isChange) { if (isChange) { this.changeAddressIndex++; } else { @@ -26,11 +36,11 @@ AddressManager.prototype._incrementIndex = function (isChange) { } }; -AddressManager.prototype.getCurrentAddressPath = function (isChange) { - return HDPath.Branch(isChange ? this.changeAddressIndex : this.receiveAddressIndex, isChange, this.copayerIndex); +AddressManager.prototype.getCurrentAddressPath = function(isChange) { + return HDPath.Branch(isChange ? this.changeAddressIndex : this.receiveAddressIndex, isChange, this.copayerIndex); }; -AddressManager.prototype.getNewAddressPath = function (isChange) { +AddressManager.prototype.getNewAddressPath = function(isChange) { var ret = this.getCurrentAddressPath(isChange); this._incrementIndex(isChange); return ret; diff --git a/lib/model/copayer.js b/lib/model/copayer.js index 4e9ce77..d9cfc31 100644 --- a/lib/model/copayer.js +++ b/lib/model/copayer.js @@ -11,11 +11,10 @@ var AddressManager = require('./addressmanager'); var Utils = require('../walletutils'); -var VERSION = '1.0.0'; var MESSAGE_SIGNING_PATH = "m/1/0"; function Copayer() { - this.version = VERSION; + this.version = '1.0.0'; }; Copayer.create = function(opts) { @@ -32,7 +31,7 @@ Copayer.create = function(opts) { x.name = opts.name; x.xPubKeySignature = opts.xPubKeySignature; // So third parties can check independently x.signingPubKey = x.getSigningPubKey(); - x.addressManager = new AddressManager({ + x.addressManager = AddressManager.create({ copayerIndex: opts.copayerIndex }); diff --git a/lib/model/notification.js b/lib/model/notification.js index 0b51988..07b621f 100644 --- a/lib/model/notification.js +++ b/lib/model/notification.js @@ -1,5 +1,3 @@ - - var Uuid = require('uuid'); /* @@ -20,25 +18,33 @@ var Uuid = require('uuid'); * { amount: 'xxx', address: 'xxx'} * { txProposalId: 'xxx', copayerId: 'xxx' } * - * Data is meant to provide only the needed information - * to notify the user + * Data is meant to provide only the needed information + * to notify the user * */ +function Notification() { + this.version = '1.0.0'; +}; -function Notification(opts) { +Notification.create = function(opts) { opts = opts || {}; + var x = new Notification(); + var now = Date.now(); - this.createdOn = Math.floor(now / 1000); - this.id = ('00000000000000' + now).slice(-14) + ('0000' + opts.ticker||0).slice(-4) ; - this.type = opts.type || 'general'; - this.data = opts.data; + x.createdOn = Math.floor(now / 1000); + x.id = ('00000000000000' + now).slice(-14) + ('0000' + opts.ticker || 0).slice(-4); + x.type = opts.type || 'general'; + x.data = opts.data; + + return x; }; Notification.fromObj = function(obj) { - var x= new Notification(); + var x = new Notification(); x.createdOn = obj.createdOn; + x.id = obj.id; x.type = obj.type, x.data = obj.data; diff --git a/lib/model/txproposal.js b/lib/model/txproposal.js index eac5480..fb04a9b 100644 --- a/lib/model/txproposal.js +++ b/lib/model/txproposal.js @@ -7,28 +7,32 @@ var Address = Bitcore.Address; var TxProposalAction = require('./txproposalaction'); -var VERSION = '1.0.0'; +function TxProposal() { + this.version = '1.0.0'; +}; -function TxProposal(opts) { +TxProposal.create = function(opts) { opts = opts || {}; - this.version = VERSION; + var x = new TxProposal(); var now = Date.now(); - this.createdOn = Math.floor(now / 1000); - this.id = ('00000000000000' + now).slice(-14) + Uuid.v4(); - this.creatorId = opts.creatorId; - this.toAddress = opts.toAddress; - this.amount = opts.amount; - this.message = opts.message; - this.proposalSignature = opts.proposalSignature; - this.changeAddress = opts.changeAddress; - this.inputs = opts.inputs; - this.inputPaths = opts.inputPaths; - this.requiredSignatures = opts.requiredSignatures; - this.requiredRejections = opts.requiredRejections; - this.status = 'pending'; - this.actions = {}; + x.createdOn = Math.floor(now / 1000); + x.id = ('00000000000000' + now).slice(-14) + Uuid.v4(); + x.creatorId = opts.creatorId; + x.toAddress = opts.toAddress; + x.amount = opts.amount; + x.message = opts.message; + x.proposalSignature = opts.proposalSignature; + x.changeAddress = opts.changeAddress; + x.inputs = []; + x.inputPaths = []; + x.requiredSignatures = opts.requiredSignatures; + x.requiredRejections = opts.requiredRejections; + x.status = 'pending'; + x.actions = {}; + + return x; }; TxProposal.fromObj = function(obj) { @@ -51,7 +55,7 @@ TxProposal.fromObj = function(obj) { x.inputPaths = obj.inputPaths; x.actions = obj.actions; _.each(x.actions, function(action, copayerId) { - x.actions[copayerId] = new TxProposalAction(action); + x.actions[copayerId] = TxProposalAction.fromObj(action); }); return x; @@ -136,7 +140,7 @@ TxProposal.prototype.getActionBy = function(copayerId) { }; TxProposal.prototype.addAction = function(copayerId, type, comment, signatures, xpub) { - var action = new TxProposalAction({ + var action = TxProposalAction.create({ copayerId: copayerId, type: type, signatures: signatures, diff --git a/lib/model/txproposalaction.js b/lib/model/txproposalaction.js index a215657..8e6dd31 100644 --- a/lib/model/txproposalaction.js +++ b/lib/model/txproposalaction.js @@ -1,14 +1,22 @@ 'use strict'; -function TxProposalAction(opts) { +function TxProposalAction() { + this.version = '1.0.0'; +}; + +TxProposalAction.create = function(opts) { opts = opts || {}; - this.createdOn = Math.floor(Date.now() / 1000); - this.copayerId = opts.copayerId; - this.type = opts.type || (opts.signatures ? 'accept' : 'reject'); - this.signatures = opts.signatures; - this.xpub = opts.xpub; - this.comment = opts.comment; + var x = new TxProposalAction(); + + x.createdOn = Math.floor(Date.now() / 1000); + x.copayerId = opts.copayerId; + x.type = opts.type || (opts.signatures ? 'accept' : 'reject'); + x.signatures = opts.signatures; + x.xpub = opts.xpub; + x.comment = opts.comment; + + return x; }; TxProposalAction.fromObj = function(obj) { diff --git a/lib/model/wallet.js b/lib/model/wallet.js index 634312a..6c20903 100644 --- a/lib/model/wallet.js +++ b/lib/model/wallet.js @@ -10,11 +10,8 @@ var Copayer = require('./copayer'); var AddressManager = require('./addressmanager'); var WalletUtils = require('../walletutils'); -var VERSION = '1.0.0'; - - function Wallet() { - this.version = VERSION; + this.version = '1.0.0'; }; Wallet.create = function(opts) { @@ -33,7 +30,7 @@ Wallet.create = function(opts) { x.copayers = []; x.pubKey = opts.pubKey; x.network = opts.network; - x.addressManager = new AddressManager(); + x.addressManager = AddressManager.create(); return x; }; @@ -125,7 +122,7 @@ Wallet.prototype.createAddress = function(isChange) { $.checkState(this.isComplete()); var path = this.addressManager.getNewAddressPath(isChange); - return new Address(WalletUtils.deriveAddress(this.publicKeyRing, path, this.m, this.network)); + return Address.create(WalletUtils.deriveAddress(this.publicKeyRing, path, this.m, this.network)); }; diff --git a/lib/server.js b/lib/server.js index 18d61f1..71f77dc 100644 --- a/lib/server.js +++ b/lib/server.js @@ -169,7 +169,7 @@ CopayServer.prototype._notify = function(type, data) { var walletId = self.walletId || data.walletId; $.checkState(walletId); - var n = new Notification({ + var n = Notification.create({ type: type, data: data, ticker: this.notifyTicker++, @@ -496,7 +496,7 @@ CopayServer.prototype.createTx = function(opts, cb) { locked: true }); - var txp = new TxProposal({ + var txp = TxProposal.create({ creatorId: self.copayerId, toAddress: opts.toAddress, amount: opts.amount, diff --git a/test/addressmanager.js b/test/addressmanager.js index 3f0a7fb..d496a87 100644 --- a/test/addressmanager.js +++ b/test/addressmanager.js @@ -10,7 +10,7 @@ var AddressManager = require('../lib/model/addressmanager'); describe('AddressManager', function() { describe('#getCurrentAddressPath', function() { it('should return a valid BIP32 path for given index', function() { - var am = new AddressManager({ + var am = AddressManager.create({ copayerIndex: 4 }); am.getCurrentAddressPath(false).should.equal('m/4/0/0'); @@ -19,14 +19,14 @@ describe('AddressManager', function() { }); describe('#getCurrentAddressPath', function() { it('should return a valid BIP32 path for defaut Index', function() { - var am = new AddressManager(); + var am = AddressManager.create(); am.getCurrentAddressPath(false).should.equal('m/2147483647/0/0'); am.getCurrentAddressPath(true).should.equal('m/2147483647/1/0'); }); }); describe('#getNewAddressPath', function() { it('should return a new valid BIP32 path for given index', function() { - var am = new AddressManager({ + var am = AddressManager.create({ copayerIndex: 2 }); am.getNewAddressPath(false).should.equal('m/2/0/0');