Browse Source

test regaining access to wallet with only xPrivKey

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

16
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 (!!self.credentials.walletPrivKey) {
if (!Verifier.checkCopayers(self.credentials, wallet.copayers)) { if (!Verifier.checkCopayers(self.credentials, wallet.copayers)) {
return cb(new ServerCompromisedError( return cb(new ServerCompromisedError(
'Copayers in the wallet could not be verified to have known the wallet secret')); '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) {

85
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,7 +868,9 @@ describe('client API ', function() {
it.skip('should get paginated transaction history', function(done) {}); it.skip('should get paginated transaction history', function(done) {});
}); });
describe('Mobility, backup & recovery', function() {
describe('Export & Import', function() { describe('Export & Import', function() {
describe('Success', function() {
var address, importedClient; var address, importedClient;
beforeEach(function(done) { beforeEach(function(done) {
importedClient = null; importedClient = null;
@ -891,9 +896,7 @@ describe('client API ', function() {
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() { it.skip('should export & import compressed', function() {
@ -905,9 +908,7 @@ describe('client API ', function() {
compressed: true compressed: true
}); });
importedClient = new Client({ importedClient = helpers.newClient(app);
request: helpers.getRequest(app),
});
importedClient.import(exported, { importedClient.import(exported, {
compressed: true compressed: true
}); });
@ -924,9 +925,7 @@ describe('client API ', function() {
}); });
exported.should.not.contain(xPrivKey); exported.should.not.contain(xPrivKey);
importedClient = new Client({ importedClient = helpers.newClient(app);
request: helpers.getRequest(app),
});
importedClient.import(exported, { importedClient.import(exported, {
password: '123' password: '123'
}); });
@ -939,19 +938,51 @@ describe('client API ', function() {
password: '123' password: '123'
}); });
importedClient = new Client({ importedClient = helpers.newClient(app);
request: helpers.getRequest(app),
});
importedClient.import(exported, { importedClient.import(exported, {
compressed: true, compressed: true,
password: '123' password: '123'
}); });
}); });
});
describe('Fail', function() {
it.skip('should fail to export compressed & import uncompressed', function() {}); 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 uncompressed & import compressed', function() {});
it.skip('should fail to export unencrypted & import with password', function() {}); it.skip('should fail to export unencrypted & import with password', function() {});
it.skip('should fail to export encrypted & import with incorrect password', function() {}); it.skip('should fail to export encrypted & import with incorrect password', function() {});
}); });
});
describe('Recovery', function() {
it('should be able to regain access to a 1-1 wallet with just the xPriv', function(done) {
helpers.createAndJoinWallet(clients, 1, 1, function() {
var xpriv = clients[0].credentials.xPrivKey;
var walletName = clients[0].credentials.walletName;
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();
});
});
});
});
});
});
});
describe('Air gapped related flows', function() { describe('Air gapped related flows', function() {
it('should create wallet in proxy from airgapped', function(done) { it('should create wallet in proxy from airgapped', function(done) {
@ -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