Merge pull request #699 from yemel/feature/one-time-backups
Add wallet addresses index discovery on importing backup
This commit is contained in:
commit
50823ebd1c
8 changed files with 222 additions and 18 deletions
|
|
@ -6,21 +6,16 @@ angular.module('copayApp.controllers').controller('ImportController',
|
|||
var reader = new FileReader();
|
||||
var _importBackup = function(encryptedObj) {
|
||||
Passphrase.getBase64Async($scope.password, function(passphrase){
|
||||
var w, errMsg;
|
||||
try {
|
||||
w = walletFactory.fromEncryptedObj(encryptedObj, passphrase);
|
||||
} catch(e) {
|
||||
errMsg = e.message;
|
||||
}
|
||||
if (!w) {
|
||||
$scope.loading = false;
|
||||
$rootScope.$flashMessage = { message: errMsg || 'Wrong password', type: 'error'};
|
||||
$rootScope.$digest();
|
||||
return;
|
||||
}
|
||||
$rootScope.wallet = w;
|
||||
|
||||
controllerUtils.startNetwork($rootScope.wallet, $scope);
|
||||
walletFactory.import(encryptedObj, passphrase, function(err, w) {
|
||||
if (err) {
|
||||
$scope.loading = false;
|
||||
$rootScope.$flashMessage = { message: err.errMsg || 'Wrong password', type: 'error'};
|
||||
$rootScope.$digest();
|
||||
return;
|
||||
}
|
||||
$rootScope.wallet = w;
|
||||
controllerUtils.startNetwork($rootScope.wallet, $scope);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -159,6 +159,27 @@ Insight.prototype.sendRawTransaction = function(rawtx, cb) {
|
|||
});
|
||||
};
|
||||
|
||||
Insight.prototype.checkActivity = function(addresses, cb) {
|
||||
if (!addresses) throw new Error('address must be set');
|
||||
|
||||
this.getTransactions(addresses, function onResult(txs) {
|
||||
var flatArray = function (xss) { return xss.reduce(function(r, xs) { return r.concat(xs); }, []); };
|
||||
var getInputs = function (t) { return t.vin.map(function (vin) { return vin.addr }); };
|
||||
var getOutputs = function (t) { return flatArray(
|
||||
t.vout.map(function (vout) { return vout.scriptPubKey.addresses; })
|
||||
);};
|
||||
|
||||
var activityMap = new Array(addresses.length);
|
||||
var activeAddress = flatArray(txs.map(function(t) { return getInputs(t).concat(getOutputs(t)); }));
|
||||
activeAddress.forEach(function (addr) {
|
||||
var index = addresses.indexOf(addr);
|
||||
if (index != -1) activityMap[index] = true;
|
||||
});
|
||||
|
||||
cb(null, activityMap);
|
||||
});
|
||||
};
|
||||
|
||||
Insight.prototype._request = function(options, callback) {
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ AddressIndex.prototype.toObj = function() {
|
|||
AddressIndex.prototype.checkRange = function(index, isChange) {
|
||||
if ((isChange && index > this.changeIndex) ||
|
||||
(!isChange && index > this.receiveIndex)) {
|
||||
throw new Error('Out of bounds at index %d isChange: %d', index, isChange);
|
||||
throw new Error('Out of bounds at index ' + index + ' isChange: ' + isChange);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -157,8 +157,6 @@ PublicKeyRing.prototype.getPubKeys = function(index, isChange) {
|
|||
|
||||
// TODO this could be cached
|
||||
PublicKeyRing.prototype.getRedeemScript = function (index, isChange) {
|
||||
this.indexes.checkRange(index, isChange);
|
||||
|
||||
var pubKeys = this.getPubKeys(index, isChange);
|
||||
var script = Script.createMultisig(this.requiredCopayers, pubKeys);
|
||||
return script;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ var coinUtil = bitcore.util;
|
|||
var buffertools = bitcore.buffertools;
|
||||
var Builder = bitcore.TransactionBuilder;
|
||||
var http = require('http');
|
||||
var async = require('async');
|
||||
var EventEmitter = imports.EventEmitter || require('events').EventEmitter;
|
||||
var copay = copay || require('../../../copay');
|
||||
var SecureRandom = bitcore.SecureRandom;
|
||||
|
|
@ -711,6 +712,71 @@ Wallet.prototype.createTxSync = function(toAddress, amountSatStr, comment, utxos
|
|||
return ntxid;
|
||||
};
|
||||
|
||||
Wallet.prototype.updateIndexes = function(callback) {
|
||||
var self = this;
|
||||
var start = self.publicKeyRing.indexes.changeIndex;
|
||||
self.indexDiscovery(start, true, 20, function(err, changeIndex) {
|
||||
if (err) return callback(err);
|
||||
if (changeIndex != -1)
|
||||
self.publicKeyRing.indexes.changeIndex = changeIndex + 1;
|
||||
|
||||
start = self.publicKeyRing.indexes.receiveIndex;
|
||||
self.indexDiscovery(start, false, 20, function(err, receiveIndex) {
|
||||
if (err) return callback(err);
|
||||
if (receiveIndex != -1)
|
||||
self.publicKeyRing.indexes.receiveIndex = receiveIndex + 1;
|
||||
|
||||
self.emit('publicKeyRingUpdated');
|
||||
self.store();
|
||||
callback();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Wallet.prototype.deriveAddresses = function(index, amout, isChange) {
|
||||
var ret = new Array(amout);
|
||||
for(var i = 0; i < amout; i++) {
|
||||
ret[i] = this.publicKeyRing.getAddress(index + i, isChange).toString();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// This function scans the publicKeyRing branch starting at index @start and reports the index with last activity,
|
||||
// using a scan window of @gap. The argument @change defines the branch to scan: internal or external.
|
||||
// Returns -1 if no activity is found in range.
|
||||
Wallet.prototype.indexDiscovery = function(start, change, gap, cb) {
|
||||
var scanIndex = start;
|
||||
var lastActive = -1;
|
||||
var hasActivity = false;
|
||||
|
||||
var self = this;
|
||||
async.doWhilst(
|
||||
function _do(next) {
|
||||
// Optimize window to minimize the derivations.
|
||||
var scanWindow = (lastActive == -1) ? gap : gap - (scanIndex - lastActive) + 1;
|
||||
var addresses = self.deriveAddresses(scanIndex, scanWindow, change);
|
||||
self.blockchain.checkActivity(addresses, function(err, actives){
|
||||
if (err) throw err;
|
||||
|
||||
// Check for new activities in the newlly scanned addresses
|
||||
var recentActive = actives.reduce(function(r, e, i) {
|
||||
return e ? scanIndex + i : r;
|
||||
}, lastActive);
|
||||
hasActivity = lastActive != recentActive;
|
||||
lastActive = recentActive;
|
||||
scanIndex += scanWindow;
|
||||
next();
|
||||
});
|
||||
},
|
||||
function _while() { return hasActivity; },
|
||||
function _finnaly(err) {
|
||||
if (err) return cb(err);
|
||||
cb(null, lastActive);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Wallet.prototype.disconnect = function() {
|
||||
this.log('## DISCONNECTING');
|
||||
this.network.disconnect();
|
||||
|
|
|
|||
|
|
@ -74,6 +74,16 @@ WalletFactory.prototype.fromEncryptedObj = function(base64, password) {
|
|||
return w;
|
||||
};
|
||||
|
||||
WalletFactory.prototype.import = function(base64, password, cb) {
|
||||
var self = this;
|
||||
var w = self.fromEncryptedObj(base64, password);
|
||||
w.updateIndexes(function(err) {
|
||||
if (err) return cb(err);
|
||||
self.log('Indexes updated');
|
||||
cb(null, w);
|
||||
});
|
||||
}
|
||||
|
||||
WalletFactory.prototype.read = function(walletId) {
|
||||
if (!this._checkRead(walletId))
|
||||
return false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue