implements skipping fields in backup imports

This commit is contained in:
Matias Alejo Garcia 2014-08-20 14:45:59 -04:00
parent fea90215d4
commit 7f0e9cd03d
7 changed files with 169 additions and 113 deletions

View File

@ -21,5 +21,4 @@ angular.module('copayApp.controllers').controller('BackupController',
controllerUtils.logout();
});
};
});

View File

@ -7,6 +7,10 @@ angular.module('copayApp.controllers').controller('ImportController',
$scope.title = 'Import a backup';
$scope.importStatus = 'Importing wallet - Reading backup...';
var s = ($location.search()).skip;
if (s) {
$scope.skipFields = s.split(',');
}
var reader = new FileReader();
@ -22,7 +26,7 @@ angular.module('copayApp.controllers').controller('ImportController',
// try to import encrypted wallet with passphrase
try {
w = walletFactory.import(encryptedObj, passphrase);
w = walletFactory.import(encryptedObj, passphrase, $scope.skipFields);
} catch (e) {
errMsg = e.message;
}

View File

@ -239,7 +239,7 @@ PublicKeyRing.prototype.getCosigner = function(pubKey) {
});
var index = sorted.indexOf(pubKey);
if (index == -1) throw new Error('no public key in ring');
if (index == -1) throw new Error('public key is not on the ring');
return index;
}

View File

@ -535,9 +535,33 @@ Wallet.fromObj = function(o, storage, network, blockchain) {
var opts = JSON.parse(JSON.stringify(o.opts));
opts.addressBook = o.addressBook;
opts.publicKeyRing = PublicKeyRing.fromObj(o.publicKeyRing);
opts.txProposals = TxProposals.fromObj(o.txProposals, Wallet.builderOpts);
if (o.privateKey)
opts.privateKey = PrivateKey.fromObj(o.privateKey);
else
opts.privateKey = new PrivateKey({
networkName: this.networkName
});
if (o.publicKeyRing)
opts.publicKeyRing = PublicKeyRing.fromObj(o.publicKeyRing);
else {
opts.publicKeyRing = new PublicKeyRing({
networkName: opts.networkName,
requiredCopayers: opts.requiredCopayers,
totalCopayers: opts.totalCopayers,
});
opts.publicKeyRing.addCopayer(
opts.privateKey.deriveBIP45Branch().extendedPublicKeyString(),
opts.nickname
);
}
if (o.txProposals)
opts.txProposals = TxProposals.fromObj(o.txProposals, Wallet.builderOpts);
else
opts.txProposals = new TxProposals({
networkName: this.networkName,
});
opts.storage = storage;
opts.network = network;

View File

@ -50,11 +50,19 @@ WalletFactory.prototype._checkRead = function(walletId) {
return !!ret;
};
WalletFactory.prototype.fromObj = function(obj) {
WalletFactory.prototype.fromObj = function(obj, skipFields) {
// not stored options
obj.opts.reconnectDelay = this.walletDefaults.reconnectDelay;
skipFields = skipFields || [];
skipFields.forEach(function(k){
if (obj[k])
delete obj[k];
else
throw new Error('unknown field:' + k);
});
var w = Wallet.fromObj(obj, this.storage, this.network, this.blockchain);
if (!w) return false;
w.verbose = this.verbose;
@ -63,23 +71,23 @@ WalletFactory.prototype.fromObj = function(obj) {
return w;
};
WalletFactory.prototype.fromEncryptedObj = function(base64, password) {
WalletFactory.prototype.fromEncryptedObj = function(base64, password, skipFields) {
this.storage._setPassphrase(password);
var walletObj = this.storage.import(base64);
if (!walletObj) return false;
var w = this.fromObj(walletObj);
var w = this.fromObj(walletObj, skipFields);
return w;
};
WalletFactory.prototype.import = function(base64, password) {
WalletFactory.prototype.import = function(base64, password, skipFields) {
var self = this;
var w = self.fromEncryptedObj(base64, password);
var w = self.fromEncryptedObj(base64, password, skipFields);
if (!w) throw new Error('Wrong password');
return w;
}
WalletFactory.prototype.read = function(walletId) {
WalletFactory.prototype.read = function(walletId, skipFields) {
if (!this._checkRead(walletId))
return false;
@ -94,7 +102,7 @@ WalletFactory.prototype.read = function(walletId) {
obj.addressBook = s.get(walletId, 'addressBook');
obj.backupOffered = s.get(walletId, 'backupOffered');
var w = this.fromObj(obj);
var w = this.fromObj(obj, skipFields);
return w;
};

View File

@ -175,6 +175,19 @@ describe('WalletFactory model', function() {
assertObjectEqual(w.toObj(), JSON.parse(o));
});
it('#fromObj, skipping fields', function() {
var wf = new WalletFactory(config, '0.0.5');
var w = wf.fromObj(JSON.parse(o), ['publicKeyRing']);
should.exist(w);
w.id.should.equal("dbfe10c3fae71cea");
should.exist(w.publicKeyRing.getCopayerId);
should.exist(w.txProposals.toObj());
should.exist(w.privateKey.toObj());
(function() { assertObjectEqual(w.toObj(), JSON.parse(o))}).should.throw();
});
it('support old index schema: #fromObj #toObj round trip', function() {
var o = '{"opts":{"id":"dbfe10c3fae71cea","spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":{"changeIndex":0,"receiveIndex":0},"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{}}';
var o2 = '{"opts":{"id":"dbfe10c3fae71cea","spendUnconfirmed":1,"requiredCopayers":3,"totalCopayers":5,"version":"0.0.5"},"networkNonce":"0000000000000001","networkNonces":[],"publicKeyRing":{"walletId":"dbfe10c3fae71cea","networkName":"testnet","requiredCopayers":3,"totalCopayers":5,"indexes":[{"copayerIndex":2147483647,"changeIndex":0,"receiveIndex":0},{"copayerIndex":0,"changeIndex":0,"receiveIndex":0},{"copayerIndex":1,"changeIndex":0,"receiveIndex":0},{"copayerIndex":2,"changeIndex":0,"receiveIndex":0},{"copayerIndex":3,"changeIndex":0,"receiveIndex":0},{"copayerIndex":4,"changeIndex":0,"receiveIndex":0}],"copayersBackup":[],"copayersExtPubKeys":["tpubD6NzVbkrYhZ4YGK8ZhZ8WVeBXNAAoTYjjpw9twCPiNGrGQYFktP3iVQkKmZNiFnUcAFMJRxJVJF6Nq9MDv2kiRceExJaHFbxUCGUiRhmy97","tpubD6NzVbkrYhZ4YKGDJkzWdQsQV3AcFemaQKiwNhV4RL8FHnBFvinidGdQtP8RKj3h34E65RkdtxjrggZYqsEwJ8RhhN2zz9VrjLnrnwbXYNc","tpubD6NzVbkrYhZ4YkDiewjb32Pp3Sz9WK2jpp37KnL7RCrHAyPpnLfgdfRnTdpn6DTWmPS7niywfgWiT42aJb1J6CjWVNmkgsMCxuw7j9DaGKB","tpubD6NzVbkrYhZ4XEtUAz4UUTWbprewbLTaMhR8NUvSJUEAh4Sidxr6rRPFdqqVRR73btKf13wUjds2i8vVCNo8sbKrAnyoTr3o5Y6QSbboQjk","tpubD6NzVbkrYhZ4Yj9AAt6xUVuGPVd8jXCrEE6V2wp7U3PFh8jYYvVad31b4VUXEYXzSnkco4fktu8r4icBsB2t3pCR3WnhVLedY2hxGcPFLKD"],"nicknameFor":{}},"txProposals":{"txps":[],"walletId":"dbfe10c3fae71cea","networkName":"testnet"},"privateKey":{"extendedPrivateKeyString":"tprv8ZgxMBicQKsPeoHLg3tY75z4xLeEe8MqAXLNcRA6J6UTRvHV8VZTXznt9eoTmSk1fwSrwZtMhY3XkNsceJ14h6sCXHSWinRqMSSbY8tfhHi","networkName":"testnet"},"addressBook":{}}';

View File

@ -3,12 +3,14 @@
<i class="size-60 fi-bitcoin-circle icon-rotate spinner"></i>
{{ importStatus }}
</div>
<div class="row" ng-init="choosefile=0; pastetext=0" ng-show="!loading">
<div class="large-4 columns logo-setup">
<img src="img/logo-negative-beta.svg" alt="Copay" width="146" height="59">
<div ng-include="'views/includes/version.html'"></div>
</div>
<div class="large-8 columns line-dashed-setup-v">
<div class="box-setup">
<h1 class="text-white line-sidebar-b">{{title}}</h1>
<form name="importForm" ng-submit="import(importForm)" novalidate>
@ -20,6 +22,12 @@
<label for="password">Password <small>Required</small></label>
<input type="password" class="form-control" placeholder="Your wallet password" name="password" ng-model="password" required>
<div data-alert ng-show="skipFields">
<i class="size-18 fi-alert"></i>
Skipping fields: {{skipFields}}
</div>
<div class="text-right">
<a class="back-button text-white m20r" href="#!/">&laquo; Back</a>
<button type="submit" class="button primary m0" ng-disabled="importForm.$invalid" loading="Importing">