Merge pull request #849 from yemel/feature/backup-wallet
Wallet Backups
This commit is contained in:
commit
5d5bf3dfc5
12 changed files with 166 additions and 45 deletions
|
|
@ -87,9 +87,9 @@ angular.module('copayApp.controllers').controller('HeaderController',
|
|||
window.onbeforeunload = undefined;
|
||||
});
|
||||
|
||||
$scope.backupAndOpen = function() {
|
||||
$scope.backup = function() {
|
||||
var w = $rootScope.wallet;
|
||||
w.offerBackup();
|
||||
w.setBackupReady();
|
||||
backupService.download(w);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,12 @@ angular.module('copayApp.controllers').controller('ImportController',
|
|||
return;
|
||||
}
|
||||
|
||||
if (!w.isReady()) {
|
||||
$rootScope.wallet = w;
|
||||
controllerUtils.startNetwork($rootScope.wallet, $scope);
|
||||
return;
|
||||
}
|
||||
|
||||
w.updateIndexes(function(err) {
|
||||
updateStatus('Importing wallet - We are almost there...');
|
||||
if (err) {
|
||||
|
|
|
|||
|
|
@ -81,6 +81,9 @@ angular.module('copayApp.controllers').controller('SetupController',
|
|||
passphrase: passphrase,
|
||||
};
|
||||
var w = walletFactory.create(opts);
|
||||
if (w.totalCopayers > 1) {
|
||||
backupService.download(w);
|
||||
}
|
||||
controllerUtils.startNetwork(w, $scope);
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ angular.module('copayApp.controllers').controller('SigninController',
|
|||
notification.error('Unknown error');
|
||||
controllerUtils.onErrorDigest();
|
||||
} else {
|
||||
backupService.download(w);
|
||||
controllerUtils.startNetwork(w, $scope);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ function PublicKeyRing(opts) {
|
|||
this.publicKeysCache = opts.publicKeysCache || {};
|
||||
this.nicknameFor = opts.nicknameFor || {};
|
||||
this.copayerIds = [];
|
||||
this.copayersBackup = opts.copayersBackup || [];
|
||||
this.addressToPath = {};
|
||||
}
|
||||
|
||||
|
|
@ -59,6 +60,7 @@ PublicKeyRing.prototype.toObj = function() {
|
|||
requiredCopayers: this.requiredCopayers,
|
||||
totalCopayers: this.totalCopayers,
|
||||
indexes: AddressIndex.serialize(this.indexes),
|
||||
copayersBackup: this.copayersBackup,
|
||||
|
||||
copayersExtPubKeys: this.copayersHK.map(function(b) {
|
||||
return b.extendedPublicKeyString();
|
||||
|
|
@ -78,7 +80,11 @@ PublicKeyRing.prototype.registeredCopayers = function() {
|
|||
};
|
||||
|
||||
PublicKeyRing.prototype.isComplete = function() {
|
||||
return this.registeredCopayers() === this.totalCopayers;
|
||||
return this.remainingCopayers() == 0;
|
||||
};
|
||||
|
||||
PublicKeyRing.prototype.remainingCopayers = function() {
|
||||
return this.totalCopayers - this.registeredCopayers();
|
||||
};
|
||||
|
||||
PublicKeyRing.prototype.getAllCopayerIds = function() {
|
||||
|
|
@ -350,12 +356,34 @@ PublicKeyRing.prototype._mergePubkeys = function(inPKR) {
|
|||
return hasChanged;
|
||||
};
|
||||
|
||||
PublicKeyRing.prototype.setBackupReady = function(copayerId) {
|
||||
if (this.isBackupReady()) return false;
|
||||
|
||||
var cid = this.myCopayerId();
|
||||
this.copayersBackup.push(cid);
|
||||
return true;
|
||||
}
|
||||
|
||||
PublicKeyRing.prototype.isBackupReady = function(copayerId) {
|
||||
var cid = copayerId || this.myCopayerId();
|
||||
return this.copayersBackup.indexOf(cid) != -1;
|
||||
}
|
||||
|
||||
PublicKeyRing.prototype.isFullyBackup = function(copayerId) {
|
||||
return this.remainingBackups() == 0;
|
||||
}
|
||||
|
||||
PublicKeyRing.prototype.remainingBackups = function() {
|
||||
return this.totalCopayers - this.copayersBackup.length;
|
||||
};
|
||||
|
||||
PublicKeyRing.prototype.merge = function(inPKR, ignoreId) {
|
||||
this._checkInPKR(inPKR, ignoreId);
|
||||
|
||||
var hasChanged = false;
|
||||
hasChanged |= this.mergeIndexes(inPKR.indexes);
|
||||
hasChanged |= this._mergePubkeys(inPKR);
|
||||
hasChanged |= this.mergeBackups(inPKR.copayersBackup);
|
||||
|
||||
return !!hasChanged;
|
||||
};
|
||||
|
|
@ -372,4 +400,18 @@ PublicKeyRing.prototype.mergeIndexes = function(indexes) {
|
|||
return !!hasChanged
|
||||
}
|
||||
|
||||
PublicKeyRing.prototype.mergeBackups = function(backups) {
|
||||
var self = this;
|
||||
var hasChanged = false;
|
||||
|
||||
backups.forEach(function(cid) {
|
||||
var isNew = self.copayersBackup.indexOf(cid) == -1;
|
||||
if (isNew) self.copayersBackup.push(cid);
|
||||
hasChanged |= isNew;
|
||||
});
|
||||
|
||||
return !!hasChanged
|
||||
}
|
||||
|
||||
|
||||
module.exports = require('soop')(PublicKeyRing);
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ function Wallet(opts) {
|
|||
this.network.maxPeers = this.totalCopayers;
|
||||
this.registeredPeerIds = [];
|
||||
this.addressBook = opts.addressBook || {};
|
||||
this.backupOffered = opts.backupOffered || false;
|
||||
this.publicKey = this.privateKey.publicHex;
|
||||
}
|
||||
|
||||
|
|
@ -327,6 +326,7 @@ Wallet.prototype.getRegisteredPeerIds = function() {
|
|||
var pid = this.network.peerFromCopayer(cid);
|
||||
this.registeredPeerIds.push({
|
||||
peerId: pid,
|
||||
copayerId: cid,
|
||||
nick: this.publicKeyRing.nicknameForCopayer(cid),
|
||||
index: i,
|
||||
});
|
||||
|
|
@ -349,7 +349,6 @@ Wallet.prototype.toObj = function() {
|
|||
txProposals: this.txProposals.toObj(),
|
||||
privateKey: this.privateKey ? this.privateKey.toObj() : undefined,
|
||||
addressBook: this.addressBook,
|
||||
backupOffered: this.backupOffered,
|
||||
};
|
||||
|
||||
return walletObj;
|
||||
|
|
@ -358,7 +357,6 @@ Wallet.prototype.toObj = function() {
|
|||
Wallet.fromObj = function(o, storage, network, blockchain) {
|
||||
var opts = JSON.parse(JSON.stringify(o.opts));
|
||||
opts.addressBook = o.addressBook;
|
||||
opts.backupOffered = o.backupOffered;
|
||||
|
||||
opts.publicKeyRing = PublicKeyRing.fromObj(o.publicKeyRing);
|
||||
opts.txProposals = TxProposals.fromObj(o.txProposals);
|
||||
|
|
@ -879,12 +877,13 @@ Wallet.prototype.toggleAddressBookEntry = function(key) {
|
|||
};
|
||||
|
||||
Wallet.prototype.isReady = function() {
|
||||
var ret = this.publicKeyRing.isComplete() && this.backupOffered;
|
||||
var ret = this.publicKeyRing.isComplete() && this.publicKeyRing.isFullyBackup();
|
||||
return ret;
|
||||
};
|
||||
|
||||
Wallet.prototype.offerBackup = function() {
|
||||
this.backupOffered = true;
|
||||
Wallet.prototype.setBackupReady = function() {
|
||||
this.publicKeyRing.setBackupReady();
|
||||
this.sendPublicKeyRing();
|
||||
this.store();
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -11,10 +11,12 @@ BackupService.prototype.getName = function(wallet) {
|
|||
|
||||
BackupService.prototype.download = function(wallet) {
|
||||
var ew = wallet.toEncryptedObj();
|
||||
var timestamp = +(new Date());
|
||||
var walletName = this.getName(wallet);
|
||||
var filename = walletName + '-' + timestamp + '-keybackup.json.aes';
|
||||
this.notifications.success('Backup created', 'Encrypted backup file saved.');
|
||||
var partial = !wallet.publicKeyRing.isComplete();
|
||||
var walletName = this.getName(wallet) + (partial ? '-Partial' : '');
|
||||
var filename = walletName + '-keybackup.json.aes';
|
||||
|
||||
var notify = partial ? 'Partial backup created' : 'Backup created';
|
||||
this.notifications.success(notify, 'Encrypted backup file saved.');
|
||||
var blob = new Blob([ew], {
|
||||
type: 'text/plain;charset=utf-8'
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue