Browse Source

test regaining access to wallet with only xPrivKey

activeAddress
Ivan Socolsky 10 years ago
parent
commit
f486ecacd3
  1. 22
      lib/client/api.js
  2. 4
      lib/client/credentials.js
  3. 1
      lib/client/verifier.js
  4. 207
      test/integration/client.js

22
lib/client/api.js

@ -92,7 +92,7 @@ function API(opts) {
util.inherits(API, events.EventEmitter); util.inherits(API, events.EventEmitter);
API.prototype.seedFromExtendedPrivateKey = function(xPrivKey) { API.prototype.seedFromExtendedPrivateKey = function(xPrivKey) {
this.credentials = Credentials.seedFromExtendedPrivateKey(xPrivKey); this.credentials = Credentials.fromExtendedPrivateKey(xPrivKey);
}; };
API.prototype.seedFromAirGapped = function(seed) { API.prototype.seedFromAirGapped = function(seed) {
@ -254,15 +254,25 @@ API.prototype.openWallet = function(cb) {
if (err) return cb(err); if (err) return cb(err);
var wallet = ret.wallet; var wallet = ret.wallet;
if (wallet.status != 'complete') if (wallet.status != 'complete') return cb('Wallet Incomplete');
return cb('Wallet Incomplete');
if (!Verifier.checkCopayers(self.credentials, wallet.copayers)) { if (!!self.credentials.walletPrivKey) {
return cb(new ServerCompromisedError( if (!Verifier.checkCopayers(self.credentials, wallet.copayers)) {
'Copayers in the wallet could not be verified to have known the wallet secret')); return cb(new ServerCompromisedError(
'Copayers in the wallet could not be verified to have known the wallet secret'));
}
} else {
log.warn('Could not perform verification of other copayers in the wallet');
} }
self.credentials.addPublicKeyRing(_.pluck(wallet.copayers, 'xPubKey')); self.credentials.addPublicKeyRing(_.pluck(wallet.copayers, 'xPubKey'));
if (!self.credentials.hasWalletInfo()) {
var me = _.find(wallet.copayers, {
id: self.credentials.copayerId
});
self.credentials.addWalletInfo(wallet.id, wallet.name, wallet.m, wallet.n, null, me.name);
}
return cb(null, true); return cb(null, true);
}); });
}; };

4
lib/client/credentials.js

@ -105,6 +105,10 @@ Credentials.prototype.addWalletInfo = function(walletId, walletName, m, n, walle
} }
}; };
Credentials.prototype.hasWalletInfo = function() {
return !!this.walletId;
};
Credentials.prototype.addPublicKeyRing = function(publicKeyRing) { Credentials.prototype.addPublicKeyRing = function(publicKeyRing) {
this.publicKeyRing = _.clone(publicKeyRing); this.publicKeyRing = _.clone(publicKeyRing);
}; };

1
lib/client/verifier.js

@ -18,6 +18,7 @@ Verifier.checkAddress = function(credentials, address) {
}; };
Verifier.checkCopayers = function(credentials, copayers) { Verifier.checkCopayers = function(credentials, copayers) {
$.checkState(credentials.walletPrivKey);
var walletPubKey = Bitcore.PrivateKey.fromString(credentials.walletPrivKey).toPublicKey().toString(); var walletPubKey = Bitcore.PrivateKey.fromString(credentials.walletPrivKey).toPublicKey().toString();
if (copayers.length != credentials.n) { if (copayers.length != credentials.n) {

207
test/integration/client.js

@ -1,6 +1,7 @@
'use strict'; 'use strict';
var _ = require('lodash'); var _ = require('lodash');
var $ = require('preconditions').singleton();
var chai = require('chai'); var chai = require('chai');
var sinon = require('sinon'); var sinon = require('sinon');
var should = chai.should(); var should = chai.should();
@ -20,6 +21,7 @@ var TestData = require('../testdata');
var helpers = {}; var helpers = {};
helpers.getRequest = function(app) { helpers.getRequest = function(app) {
$.checkArgument(app);
return function(args, cb) { return function(args, cb) {
var req = request(app); var req = request(app);
var r = req[args.method](args.relUrl); var r = req[args.method](args.relUrl);
@ -38,6 +40,13 @@ helpers.getRequest = function(app) {
}; };
}; };
helpers.newClient = function(app) {
$.checkArgument(app);
return new Client({
request: helpers.getRequest(app),
});
};
helpers.createAndJoinWallet = function(clients, m, n, cb) { helpers.createAndJoinWallet = function(clients, m, n, cb) {
clients[0].createWallet('wallet name', 'creator', m, n, 'testnet', clients[0].createWallet('wallet name', 'creator', m, n, 'testnet',
function(err, secret) { function(err, secret) {
@ -148,9 +157,7 @@ describe('client API ', function() {
}); });
// Generates 5 clients // Generates 5 clients
clients = _.map(_.range(5), function(i) { clients = _.map(_.range(5), function(i) {
return new Client({ return helpers.newClient(app);
request: helpers.getRequest(app),
});
}); });
blockExplorerMock.reset(); blockExplorerMock.reset();
}); });
@ -179,10 +186,8 @@ describe('client API ', function() {
}); });
var s2 = sinon.stub(); var s2 = sinon.stub();
s2.load = sinon.stub().yields(null); s2.load = sinon.stub().yields(null);
var client = new Client({ var client = helpers.newClient(app);
storage: s2, client.storage = s2;
});
client.request = helpers.getRequest(app);
client.createWallet('1', '2', 1, 1, 'testnet', client.createWallet('1', '2', 1, 1, 'testnet',
function(err) { function(err) {
err.code.should.equal('ERROR'); err.code.should.equal('ERROR');
@ -206,10 +211,8 @@ describe('client API ', function() {
}); });
var s2 = sinon.stub(); var s2 = sinon.stub();
s2.load = sinon.stub().yields(null); s2.load = sinon.stub().yields(null);
var client = new Client({ var client = helpers.newClient(app);
storage: s2, client.storage = s2;
});
client.request = helpers.getRequest(app);
client.createWallet('1', '2', 1, 1, 'testnet', client.createWallet('1', '2', 1, 1, 'testnet',
function(err) { function(err) {
err.code.should.equal('ERROR'); err.code.should.equal('ERROR');
@ -865,92 +868,120 @@ describe('client API ', function() {
it.skip('should get paginated transaction history', function(done) {}); it.skip('should get paginated transaction history', function(done) {});
}); });
describe('Export & Import', function() { describe('Mobility, backup & recovery', function() {
var address, importedClient; describe('Export & Import', function() {
beforeEach(function(done) { describe('Success', function() {
importedClient = null; var address, importedClient;
helpers.createAndJoinWallet(clients, 1, 1, function() { beforeEach(function(done) {
clients[0].createAddress(function(err, addr) { importedClient = null;
should.not.exist(err); helpers.createAndJoinWallet(clients, 1, 1, function() {
should.exist(addr.address); clients[0].createAddress(function(err, addr) {
address = addr.address; should.not.exist(err);
done(); should.exist(addr.address);
address = addr.address;
done();
});
});
});
afterEach(function(done) {
importedClient.getMainAddresses({}, function(err, list) {
should.not.exist(err);
should.exist(list);
list.length.should.equal(1);
list[0].address.should.equal(address);
done();
});
}); });
});
});
afterEach(function(done) {
importedClient.getMainAddresses({}, function(err, list) {
should.not.exist(err);
should.exist(list);
list.length.should.equal(1);
list[0].address.should.equal(address);
done();
});
});
it('should export & import', function() { it('should export & import', function() {
var exported = clients[0].export(); var exported = clients[0].export();
importedClient = new Client({ importedClient = helpers.newClient(app);
request: helpers.getRequest(app), importedClient.import(exported);
}); });
importedClient.import(exported); it.skip('should export & import compressed', function() {
}); var walletId = clients[0].credentials.walletId;
it.skip('should export & import compressed', function() { var walletName = clients[0].credentials.walletName;
var walletId = clients[0].credentials.walletId; var copayerName = clients[0].credentials.copayerName;
var walletName = clients[0].credentials.walletName;
var copayerName = clients[0].credentials.copayerName;
var exported = clients[0].export({ var exported = clients[0].export({
compressed: true compressed: true
}); });
importedClient = new Client({ importedClient = helpers.newClient(app);
request: helpers.getRequest(app), importedClient.import(exported, {
}); compressed: true
importedClient.import(exported, { });
compressed: true importedClient.credentials.walletId.should.equal(walletId);
}); importedClient.credentials.walletName.should.equal(walletName);
importedClient.credentials.walletId.should.equal(walletId); importedClient.credentials.copayerName.should.equal(copayerName);
importedClient.credentials.walletName.should.equal(walletName); });
importedClient.credentials.copayerName.should.equal(copayerName); it('should export & import encrypted', function() {
}); var xPrivKey = clients[0].credentials.xPrivKey;
it('should export & import encrypted', function() { should.exist(xPrivKey);
var xPrivKey = clients[0].credentials.xPrivKey;
should.exist(xPrivKey);
var exported = clients[0].export({ var exported = clients[0].export({
password: '123' password: '123'
}); });
exported.should.not.contain(xPrivKey); exported.should.not.contain(xPrivKey);
importedClient = new Client({ importedClient = helpers.newClient(app);
request: helpers.getRequest(app), importedClient.import(exported, {
password: '123'
});
should.exist(importedClient.credentials.xPrivKey);
importedClient.credentials.xPrivKey.should.equal(xPrivKey);
});
it('should export & import compressed & encrypted', function() {
var exported = clients[0].export({
compressed: true,
password: '123'
});
importedClient = helpers.newClient(app);
importedClient.import(exported, {
compressed: true,
password: '123'
});
});
}); });
importedClient.import(exported, { describe('Fail', function() {
password: '123' it.skip('should fail to export compressed & import uncompressed', function() {});
it.skip('should fail to export uncompressed & import compressed', function() {});
it.skip('should fail to export unencrypted & import with password', function() {});
it.skip('should fail to export encrypted & import with incorrect password', function() {});
}); });
should.exist(importedClient.credentials.xPrivKey);
importedClient.credentials.xPrivKey.should.equal(xPrivKey);
}); });
it('should export & import compressed & encrypted', function() {
var exported = clients[0].export({
compressed: true,
password: '123'
});
importedClient = new Client({ describe('Recovery', function() {
request: helpers.getRequest(app), it('should be able to regain access to a 1-1 wallet with just the xPriv', function(done) {
}); helpers.createAndJoinWallet(clients, 1, 1, function() {
importedClient.import(exported, { var xpriv = clients[0].credentials.xPrivKey;
compressed: true, var walletName = clients[0].credentials.walletName;
password: '123' var copayerName = clients[0].credentials.copayerName;
clients[0].createAddress(function(err, addr) {
should.not.exist(err);
should.exist(addr);
var recoveryClient = helpers.newClient(app);
recoveryClient.seedFromExtendedPrivateKey(xpriv);
recoveryClient.openWallet(function(err) {
console.log(err);
should.not.exist(err);
recoveryClient.credentials.walletName.should.equal(walletName);
recoveryClient.credentials.copayerName.should.equal(copayerName);
recoveryClient.getMainAddresses({}, function(err, list) {
should.not.exist(err);
should.exist(list);
list[0].address.should.equal(addr.address);
done();
});
});
});
});
}); });
}); });
it.skip('should fail to export compressed & import uncompressed', function() {});
it.skip('should fail to export uncompressed & import compressed', function() {});
it.skip('should fail to export unencrypted & import with password', function() {});
it.skip('should fail to export encrypted & import with incorrect password', function() {});
}); });
describe('Air gapped related flows', function() { describe('Air gapped related flows', function() {
@ -960,9 +991,7 @@ describe('client API ', function() {
}); });
var seed = airgapped.getSeed(); var seed = airgapped.getSeed();
var proxy = new Client({ var proxy = helpers.newClient(app);
request: helpers.getRequest(app),
});
proxy.seedFromAirGapped(seed); proxy.seedFromAirGapped(seed);
should.not.exist(proxy.credentials.xPrivKey); should.not.exist(proxy.credentials.xPrivKey);
proxy.createWallet('wallet name', 'creator', 1, 1, 'testnet', function(err) { proxy.createWallet('wallet name', 'creator', 1, 1, 'testnet', function(err) {
@ -981,9 +1010,7 @@ describe('client API ', function() {
}); });
var seed = airgapped.getSeed(); var seed = airgapped.getSeed();
var proxy = new Client({ var proxy = helpers.newClient(app);
request: helpers.getRequest(app),
});
proxy.seedFromAirGapped(seed); proxy.seedFromAirGapped(seed);
async.waterfall([ async.waterfall([

Loading…
Cancel
Save