From 074cbb6fd885d5480b011f80fc5af53f49d9afff Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 18 Feb 2015 03:12:22 -0300 Subject: [PATCH 1/3] refactor join related funcions --- bit-wallet/bit-create | 2 +- bit-wallet/cli-utils.js | 2 +- lib/client/Verifier.js | 2 +- lib/client/api.js | 105 ++++++++++++++++++++-------------------- lib/server.js | 2 +- lib/walletutils.js | 19 ++++++++ 6 files changed, 76 insertions(+), 56 deletions(-) diff --git a/bit-wallet/bit-create b/bit-wallet/bit-create index 2f267ca..696fffb 100755 --- a/bit-wallet/bit-create +++ b/bit-wallet/bit-create @@ -9,7 +9,7 @@ program .version('0.0.1') .option('-c, --config [file]', 'Wallet config filename') .option('-h, --host [host]', 'Bitcore Wallet Service URL (eg: http://localhost:3001/copay/api') - .option('-t, --testnet', 'Create a Testnet Wallet', String) + .option('-t, --testnet', 'Create a Testnet Wallet') .usage('[options] [copayerName]') .parse(process.argv); diff --git a/bit-wallet/cli-utils.js b/bit-wallet/cli-utils.js index 0018cfe..46b0a33 100644 --- a/bit-wallet/cli-utils.js +++ b/bit-wallet/cli-utils.js @@ -20,7 +20,7 @@ Utils.parseMN = function(MN) { var n = parseInt(mn[1]); if (!m || ! n) { - die('Bad m-n parameter'); + die('Bad m-n parameter:' + MN); } return [m, n]; diff --git a/lib/client/Verifier.js b/lib/client/Verifier.js index b671c39..b5d0369 100644 --- a/lib/client/Verifier.js +++ b/lib/client/Verifier.js @@ -17,7 +17,7 @@ Verifier.checkAddress = function(data, address) { }; Verifier.checkCopayers = function(copayers, walletPrivKey, myXPrivKey, n) { - + $.checkArgument(walletPrivKey); var walletPubKey = Bitcore.PrivateKey.fromString(walletPrivKey).toPublicKey().toString(); if (copayers.length != n) { diff --git a/lib/client/api.js b/lib/client/api.js index b909b6d..80b5d36 100644 --- a/lib/client/api.js +++ b/lib/client/api.js @@ -43,9 +43,6 @@ function _signRequest(method, url, args, privKey) { return WalletUtils.signMessage(message, privKey); }; -function _createXPrivKey(network) { - return new Bitcore.HDPrivateKey(network).toString(); -}; function API(opts) { if (!opts.storage) { @@ -146,6 +143,41 @@ API.prototype._doGetRequest = function(url, data, cb) { return this._doRequest('get', url, {}, data, cb); }; + +API.prototype._initData = function(network, walletPrivKey, m, n) { + var xPrivKey = new Bitcore.HDPrivateKey(network); + var signingPrivKey = (new Bitcore.HDPrivateKey(xPrivKey)).derive('m/1/0').privateKey.toWIF(); + var xPubKey = (new Bitcore.HDPublicKey(xPrivKey)).toString(); + var copayerId = WalletUtils.xpubToCopayerId(xPubKey); + + var data = { + copayerId: copayerId, + xPrivKey: xPrivKey.toString(), + publicKeyRing: [xPubKey], + network: network, + m: m, + n: n, + signingPrivKey: signingPrivKey, + walletPrivKey: walletPrivKey.toWIF(), + }; + return data; +}; + +API.prototype._doJoinWallet = function(walletId, walletPrivKey, xPubKey, copayerName, cb) { + var args = { + walletId: walletId, + name: copayerName, + xPubKey: xPubKey, + xPubKeySignature: WalletUtils.signMessage(xPubKey, walletPrivKey), + }; + var url = '/v1/wallets/' + walletId + '/copayers'; + this._doPostRequest(url, args, {}, function(err, body) { + if (err) return cb(err); + return cb(null, body.wallet); + }); +}; + + API.prototype.createWallet = function(walletName, copayerName, m, n, network, cb) { var self = this; network = network || 'livenet'; @@ -169,59 +201,20 @@ API.prototype.createWallet = function(walletName, copayerName, m, n, network, cb if (err) return cb(err); var walletId = body.walletId; - var secret = walletId + ':' + walletPrivKey.toWIF() + ':' + (network == 'testnet' ? 'T' : 'L'); - var ret; - if (n > 1) - ret = secret; - - self._joinWallet(secret, copayerName, function(err) { - return cb(err, ret); - }); + var secret = WalletUtils.toSecret(walletId, walletPrivKey, network); + var data = self._initData(network, walletPrivKey, m, n); + self._doJoinWallet(walletId, walletPrivKey, data.publicKeyRing[0], copayerName, + function(err, wallet) { + if (err) return cb(err); + self.storage.save(data, function(err) { + return cb(err, n > 1 ? secret : null); + }); + }); }); }); }; -API.prototype._joinWallet = function(secret, copayerName, cb) { - var self = this; - - var secretSplit = secret.split(':'); - var walletId = secretSplit[0]; - - var walletPrivKey = Bitcore.PrivateKey.fromString(secretSplit[1]); - var network = secretSplit[2] == 'T' ? 'testnet' : 'livenet'; - var xPrivKey = _createXPrivKey(network); - - var xPubKey = new Bitcore.HDPublicKey(xPrivKey); - var xPubKeySignature = WalletUtils.signMessage(xPubKey.toString(), walletPrivKey); - - var signingPrivKey = (new Bitcore.HDPrivateKey(xPrivKey)).derive('m/1/0').privateKey; - var args = { - walletId: walletId, - name: copayerName, - xPubKey: xPubKey.toString(), - xPubKeySignature: xPubKeySignature, - }; - var url = '/v1/wallets/' + walletId + '/copayers'; - - this._doPostRequest(url, args, {}, function(err, body) { - var wallet = body.wallet; - var data = { - copayerId: body.copayerId, - - publicKeyRing: wallet.publicKeyRing, - network: wallet.network, - m: wallet.m, - n: wallet.n, - - xPrivKey: xPrivKey, - walletPrivKey: walletPrivKey.toWIF(), - signingPrivKey: signingPrivKey.toWIF(), - }; - self.storage.save(data, cb); - }); -}; - API.prototype.joinWallet = function(secret, copayerName, cb) { var self = this; @@ -229,7 +222,15 @@ API.prototype.joinWallet = function(secret, copayerName, cb) { if (data) return cb('Storage already contains a wallet'); - self._joinWallet(secret, copayerName, cb); + var secretData = WalletUtils.fromSecret(secret); + var data = self._initData(secretData.network, secretData.walletPrivKey); + self._doJoinWallet(secretData.walletId, secretData.walletPrivKey, data.publicKeyRing[0], copayerName, + function(err, wallet) { + if (err) return cb(err); + data.m = wallet.m; + data.n = wallet.n; + self.storage.save(data, cb); + }); }); }; diff --git a/lib/server.js b/lib/server.js index 71f77dc..8ee149c 100644 --- a/lib/server.js +++ b/lib/server.js @@ -119,7 +119,7 @@ CopayServer.prototype.createWallet = function(opts, cb) { m: opts.m, n: opts.n, network: network, - pubKey: pubKey, + pubKey: pubKey.toString(), }); self.storage.storeWallet(wallet, function(err) { diff --git a/lib/walletutils.js b/lib/walletutils.js index f603f33..bfecf60 100644 --- a/lib/walletutils.js +++ b/lib/walletutils.js @@ -61,6 +61,25 @@ WalletUtils.xPubToCopayerId = function(xpub) { return (new Bitcore.HDPublicKey(xpub)).derive(HDPath.IdBranch).publicKey.toString(); }; +WalletUtils.toSecret = function(walletId, walletPrivKey, network) { + return walletId + ':' + walletPrivKey.toWIF() + ':' + (network == 'testnet' ? 'T' : 'L'); +}; + +WalletUtils.fromSecret = function(secret) { + var secretSplit = secret.split(':'); + var walletId = secretSplit[0]; + var walletPrivKey = Bitcore.PrivateKey.fromString(secretSplit[1]); + var networkChar = secretSplit[2]; + + + return { + walletId: walletId, + walletPrivKey: walletPrivKey, + network: networkChar == 'T' ? 'testnet' : 'livenet', + }; +}; + + module.exports = WalletUtils; From ac3037f1d438571ade571b6d0997e19adef0371b Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 18 Feb 2015 03:34:11 -0300 Subject: [PATCH 2/3] add recreate --- bit-wallet/bit | 1 + bit-wallet/bit-recreate | 25 +++++++++++++++++++++++++ lib/client/api.js | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 60 insertions(+), 1 deletion(-) create mode 100755 bit-wallet/bit-recreate diff --git a/bit-wallet/bit b/bit-wallet/bit index 40b36d9..6b9a031 100755 --- a/bit-wallet/bit +++ b/bit-wallet/bit @@ -18,6 +18,7 @@ program .command('export', 'export wallet critical data') .command('import', 'import wallet critical data') .command('confirm', 'show copayer\'s data for confirmation') + .command('recreate', 'recreate a wallet on a remove server given local infomation') .parse(process.argv); diff --git a/bit-wallet/bit-recreate b/bit-wallet/bit-recreate new file mode 100755 index 0000000..3c035bc --- /dev/null +++ b/bit-wallet/bit-recreate @@ -0,0 +1,25 @@ +#!/usr/bin/env node + +var _ = require('lodash'); +var program = require('commander'); +var ClientLib = require('../lib/client'); +var utils = require('./cli-utils'); + +program + .version('0.0.1') + .option('-c, --config [file]', 'Wallet config filename') + .option('-h, --host [host]', 'Bitcore Wallet Service URL (eg: http://localhost:3001/copay/api') + .usage('[options] walletname') + .description('Creates a wallet on the remove server given the local information') + .parse(process.argv); + +var args = program.args; +if (!args[0]) + program.help(); + +var walletName = args[0]; +var client = utils.getClient(program); +client.reCreateWallet(walletName, function(err) { + utils.die(err); + console.log(' * Wallet Created.'); +}); diff --git a/lib/client/api.js b/lib/client/api.js index 80b5d36..167df6e 100644 --- a/lib/client/api.js +++ b/lib/client/api.js @@ -215,6 +215,39 @@ API.prototype.createWallet = function(walletName, copayerName, m, n, network, cb }); }; + +API.prototype.reCreateWallet = function(walletName, cb) { + var self = this; + this._loadAndCheck(function(err, data) { + if (err) return cb(err); + + var walletPrivKey = new Bitcore.PrivateKey(); + var args = { + name: walletName, + m: data.m, + n: data.n, + pubKey: walletPrivKey.toPublicKey().toString(), + network: data.network, + }; + var url = '/v1/wallets/'; + self._doPostRequest(url, args, {}, function(err, body) { + if (err) return cb(err); + + var walletId = body.walletId; + + var secret = WalletUtils.toSecret(walletId, walletPrivKey, data.network); + var i = 0; + async.each(data.publicKeyRing, function(xpub, next) { + var copayerName = 'recovered Copayer #' + i; + self._doJoinWallet(walletId, walletPrivKey, data.publicKeyRing[i++], copayerName, next); + }, function(err) { + return cb(err); + }); + }); + }); +}; + + API.prototype.joinWallet = function(secret, copayerName, cb) { var self = this; @@ -345,7 +378,7 @@ API.prototype.import = function(str, cb) { data.publicKeyRing.push(xPubKey); data.copayerId = WalletUtils.xPubToCopayerId(xPubKey); - data.m = data.publicKeyRing.length; + data.n = 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'; self.storage.save(data, cb); From 15e50b6946ecc3a0115549af4d48eafeba08f7c9 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Wed, 18 Feb 2015 03:39:57 -0300 Subject: [PATCH 3/3] add tests --- test/integration/clientApi.js | 5 +++-- test/integration/clienttestdata.js | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/integration/clientApi.js b/test/integration/clientApi.js index 27c5ac9..d1e1891 100644 --- a/test/integration/clientApi.js +++ b/test/integration/clientApi.js @@ -160,7 +160,6 @@ describe('client API ', function() { client.import(str, function(err,wallet) { should.not.exist(err); var wallet = JSON.parse(client.storage.fs.writeFile.getCall(0).args[1]); - TestData.storage.complete22.should.deep.equal(wallet); done(); @@ -169,7 +168,9 @@ describe('client API ', function() { }); }); - + describe('#recreate', function() { + it.skip('Should recreate a wallet acording stored data', function(done) {}); + }); describe('#signTxProposal ', function() { it.skip('should sign tx proposal', function(done) {}); diff --git a/test/integration/clienttestdata.js b/test/integration/clienttestdata.js index d731cb5..440a455 100644 --- a/test/integration/clienttestdata.js +++ b/test/integration/clienttestdata.js @@ -24,6 +24,7 @@ var storage = { complete22: { "xPrivKey": "xprv9s21ZrQH143K3nwnRt6W25h7smm4k4nbuN4QKnNkTMDHFcB11wJXYF78TpwQ3xKjik9M66nRd9WUiHB5C8XgoWSbpMRMc2AxpcUNUsi4thi", "m": 2, + "n": 2, "publicKeyRing": ["xpub661MyMwAqRbcGzNFbVQLh6CV6ukHuhBn4Bf4CGrQ6pFfNNdJ3pxrEVDtFHGsTzyz6Py23FhP8GWAqew3PsvnstEs2iayH1PK5Mx1bSVSEAG", "xpub661MyMwAqRbcGH2FXudWPDdrRobZ9XWTGaz18AnN1gkG8QW9ZUcn63RcK5qJJ5DXYXeAWBNqprdvvg8VHA5twmBHCUc6gWygXkwmU1Dohwh"], "copayerId": "020b41cfea5fae42050580474a195a8385b093f291af4079759851d8819383a680", "signingPrivKey": "KyhU3befBaePqHuPQNNyY1XFUgnArR3GUKZpZwV5vS7u1pcR3uzB",