Browse Source

Merge pull request #32 from matiu/feat/recreate

Feat/recreate
activeAddress
Ivan Socolsky 10 years ago
parent
commit
aa9c8e7ce6
  1. 1
      bit-wallet/bit
  2. 2
      bit-wallet/bit-create
  3. 25
      bit-wallet/bit-recreate
  4. 2
      bit-wallet/cli-utils.js
  5. 2
      lib/client/Verifier.js
  6. 126
      lib/client/api.js
  7. 2
      lib/server.js
  8. 19
      lib/walletutils.js
  9. 5
      test/integration/clientApi.js
  10. 1
      test/integration/clienttestdata.js

1
bit-wallet/bit

@ -18,6 +18,7 @@ program
.command('export', 'export wallet critical data') .command('export', 'export wallet critical data')
.command('import', 'import wallet critical data') .command('import', 'import wallet critical data')
.command('confirm', 'show copayer\'s data for confirmation') .command('confirm', 'show copayer\'s data for confirmation')
.command('recreate', 'recreate a wallet on a remove server given local infomation')
.parse(process.argv); .parse(process.argv);

2
bit-wallet/bit-create

@ -9,7 +9,7 @@ program
.version('0.0.1') .version('0.0.1')
.option('-c, --config [file]', 'Wallet config filename') .option('-c, --config [file]', 'Wallet config filename')
.option('-h, --host [host]', 'Bitcore Wallet Service URL (eg: http://localhost:3001/copay/api') .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] <walletName> <m-n> [copayerName]') .usage('[options] <walletName> <m-n> [copayerName]')
.parse(process.argv); .parse(process.argv);

25
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.');
});

2
bit-wallet/cli-utils.js

@ -20,7 +20,7 @@ Utils.parseMN = function(MN) {
var n = parseInt(mn[1]); var n = parseInt(mn[1]);
if (!m || ! n) { if (!m || ! n) {
die('Bad m-n parameter'); die('Bad m-n parameter:' + MN);
} }
return [m, n]; return [m, n];

2
lib/client/Verifier.js

@ -17,7 +17,7 @@ Verifier.checkAddress = function(data, address) {
}; };
Verifier.checkCopayers = function(copayers, walletPrivKey, myXPrivKey, n) { Verifier.checkCopayers = function(copayers, walletPrivKey, myXPrivKey, n) {
$.checkArgument(walletPrivKey);
var walletPubKey = Bitcore.PrivateKey.fromString(walletPrivKey).toPublicKey().toString(); var walletPubKey = Bitcore.PrivateKey.fromString(walletPrivKey).toPublicKey().toString();
if (copayers.length != n) { if (copayers.length != n) {

126
lib/client/api.js

@ -43,9 +43,6 @@ function _signRequest(method, url, args, privKey) {
return WalletUtils.signMessage(message, privKey); return WalletUtils.signMessage(message, privKey);
}; };
function _createXPrivKey(network) {
return new Bitcore.HDPrivateKey(network).toString();
};
function API(opts) { function API(opts) {
if (!opts.storage) { if (!opts.storage) {
@ -146,6 +143,41 @@ API.prototype._doGetRequest = function(url, data, cb) {
return this._doRequest('get', 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) { API.prototype.createWallet = function(walletName, copayerName, m, n, network, cb) {
var self = this; var self = this;
network = network || 'livenet'; network = network || 'livenet';
@ -169,59 +201,53 @@ API.prototype.createWallet = function(walletName, copayerName, m, n, network, cb
if (err) return cb(err); if (err) return cb(err);
var walletId = body.walletId; 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) { var secret = WalletUtils.toSecret(walletId, walletPrivKey, network);
return cb(err, ret); 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]); API.prototype.reCreateWallet = function(walletName, cb) {
var network = secretSplit[2] == 'T' ? 'testnet' : 'livenet'; var self = this;
var xPrivKey = _createXPrivKey(network); this._loadAndCheck(function(err, data) {
if (err) return cb(err);
var xPubKey = new Bitcore.HDPublicKey(xPrivKey); var walletPrivKey = new Bitcore.PrivateKey();
var xPubKeySignature = WalletUtils.signMessage(xPubKey.toString(), walletPrivKey); 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 signingPrivKey = (new Bitcore.HDPrivateKey(xPrivKey)).derive('m/1/0').privateKey; var walletId = body.walletId;
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 secret = WalletUtils.toSecret(walletId, walletPrivKey, data.network);
var wallet = body.wallet; var i = 0;
var data = { async.each(data.publicKeyRing, function(xpub, next) {
copayerId: body.copayerId, var copayerName = 'recovered Copayer #' + i;
self._doJoinWallet(walletId, walletPrivKey, data.publicKeyRing[i++], copayerName, next);
publicKeyRing: wallet.publicKeyRing, }, function(err) {
network: wallet.network, return cb(err);
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) { API.prototype.joinWallet = function(secret, copayerName, cb) {
var self = this; var self = this;
@ -229,7 +255,15 @@ API.prototype.joinWallet = function(secret, copayerName, cb) {
if (data) if (data)
return cb('Storage already contains a wallet'); 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);
});
}); });
}; };
@ -344,7 +378,7 @@ API.prototype.import = function(str, cb) {
data.publicKeyRing.push(xPubKey); data.publicKeyRing.push(xPubKey);
data.copayerId = WalletUtils.xPubToCopayerId(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.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); self.storage.save(data, cb);

2
lib/server.js

@ -119,7 +119,7 @@ CopayServer.prototype.createWallet = function(opts, cb) {
m: opts.m, m: opts.m,
n: opts.n, n: opts.n,
network: network, network: network,
pubKey: pubKey, pubKey: pubKey.toString(),
}); });
self.storage.storeWallet(wallet, function(err) { self.storage.storeWallet(wallet, function(err) {

19
lib/walletutils.js

@ -61,6 +61,25 @@ WalletUtils.xPubToCopayerId = function(xpub) {
return (new Bitcore.HDPublicKey(xpub)).derive(HDPath.IdBranch).publicKey.toString(); 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; module.exports = WalletUtils;

5
test/integration/clientApi.js

@ -160,7 +160,6 @@ describe('client API ', function() {
client.import(str, function(err,wallet) { client.import(str, function(err,wallet) {
should.not.exist(err); should.not.exist(err);
var wallet = JSON.parse(client.storage.fs.writeFile.getCall(0).args[1]); var wallet = JSON.parse(client.storage.fs.writeFile.getCall(0).args[1]);
TestData.storage.complete22.should.deep.equal(wallet); TestData.storage.complete22.should.deep.equal(wallet);
done(); 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() { describe('#signTxProposal ', function() {
it.skip('should sign tx proposal', function(done) {}); it.skip('should sign tx proposal', function(done) {});

1
test/integration/clienttestdata.js

@ -24,6 +24,7 @@ var storage = {
complete22: { complete22: {
"xPrivKey": "xprv9s21ZrQH143K3nwnRt6W25h7smm4k4nbuN4QKnNkTMDHFcB11wJXYF78TpwQ3xKjik9M66nRd9WUiHB5C8XgoWSbpMRMc2AxpcUNUsi4thi", "xPrivKey": "xprv9s21ZrQH143K3nwnRt6W25h7smm4k4nbuN4QKnNkTMDHFcB11wJXYF78TpwQ3xKjik9M66nRd9WUiHB5C8XgoWSbpMRMc2AxpcUNUsi4thi",
"m": 2, "m": 2,
"n": 2,
"publicKeyRing": ["xpub661MyMwAqRbcGzNFbVQLh6CV6ukHuhBn4Bf4CGrQ6pFfNNdJ3pxrEVDtFHGsTzyz6Py23FhP8GWAqew3PsvnstEs2iayH1PK5Mx1bSVSEAG", "xpub661MyMwAqRbcGH2FXudWPDdrRobZ9XWTGaz18AnN1gkG8QW9ZUcn63RcK5qJJ5DXYXeAWBNqprdvvg8VHA5twmBHCUc6gWygXkwmU1Dohwh"], "publicKeyRing": ["xpub661MyMwAqRbcGzNFbVQLh6CV6ukHuhBn4Bf4CGrQ6pFfNNdJ3pxrEVDtFHGsTzyz6Py23FhP8GWAqew3PsvnstEs2iayH1PK5Mx1bSVSEAG", "xpub661MyMwAqRbcGH2FXudWPDdrRobZ9XWTGaz18AnN1gkG8QW9ZUcn63RcK5qJJ5DXYXeAWBNqprdvvg8VHA5twmBHCUc6gWygXkwmU1Dohwh"],
"copayerId": "020b41cfea5fae42050580474a195a8385b093f291af4079759851d8819383a680", "copayerId": "020b41cfea5fae42050580474a195a8385b093f291af4079759851d8819383a680",
"signingPrivKey": "KyhU3befBaePqHuPQNNyY1XFUgnArR3GUKZpZwV5vS7u1pcR3uzB", "signingPrivKey": "KyhU3befBaePqHuPQNNyY1XFUgnArR3GUKZpZwV5vS7u1pcR3uzB",

Loading…
Cancel
Save