commit
aa9c8e7ce6
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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] <walletName> <m-n> [copayerName]')
|
||||
.parse(process.argv);
|
||||
|
||||
|
|
|
@ -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.');
|
||||
});
|
|
@ -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];
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,58 +201,52 @@ 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;
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
self._joinWallet(secret, copayerName, function(err) {
|
||||
return cb(err, ret);
|
||||
|
||||
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;
|
||||
|
||||
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 +255,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);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -344,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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {});
|
||||
|
|
|
@ -24,6 +24,7 @@ var storage = {
|
|||
complete22: {
|
||||
"xPrivKey": "xprv9s21ZrQH143K3nwnRt6W25h7smm4k4nbuN4QKnNkTMDHFcB11wJXYF78TpwQ3xKjik9M66nRd9WUiHB5C8XgoWSbpMRMc2AxpcUNUsi4thi",
|
||||
"m": 2,
|
||||
"n": 2,
|
||||
"publicKeyRing": ["xpub661MyMwAqRbcGzNFbVQLh6CV6ukHuhBn4Bf4CGrQ6pFfNNdJ3pxrEVDtFHGsTzyz6Py23FhP8GWAqew3PsvnstEs2iayH1PK5Mx1bSVSEAG", "xpub661MyMwAqRbcGH2FXudWPDdrRobZ9XWTGaz18AnN1gkG8QW9ZUcn63RcK5qJJ5DXYXeAWBNqprdvvg8VHA5twmBHCUc6gWygXkwmU1Dohwh"],
|
||||
"copayerId": "020b41cfea5fae42050580474a195a8385b093f291af4079759851d8819383a680",
|
||||
"signingPrivKey": "KyhU3befBaePqHuPQNNyY1XFUgnArR3GUKZpZwV5vS7u1pcR3uzB",
|
||||
|
|
Loading…
Reference in New Issue