From 41af549ed285a5abe298faf4be176d1446ec5da0 Mon Sep 17 00:00:00 2001 From: Matias Alejo Garcia Date: Fri, 9 Feb 2018 13:07:41 -0300 Subject: [PATCH 1/2] Add interrupted scan cache + status --- lib/errors/errordefinitions.js | 1 + lib/model/addressmanager.js | 12 ++++ lib/server.js | 51 +++++++++++++++-- lib/storage.js | 32 +++++++++++ test/integration/helpers.js | 15 ++++- test/integration/server.js | 100 ++++++++++++++++++++++++++++++++- 6 files changed, 201 insertions(+), 10 deletions(-) diff --git a/lib/errors/errordefinitions.js b/lib/errors/errordefinitions.js index a69017e..26c0006 100644 --- a/lib/errors/errordefinitions.js +++ b/lib/errors/errordefinitions.js @@ -36,6 +36,7 @@ var errors = { WALLET_BUSY: 'Wallet is busy, try later', WALLET_NOT_COMPLETE: 'Wallet is not complete', WALLET_NOT_FOUND: 'Wallet not found', + WALLET_NEED_SCAN: 'Wallet needs addresses scan', }; var errorObjects = _.zipObject(_.map(errors, function(msg, code) { diff --git a/lib/model/addressmanager.js b/lib/model/addressmanager.js index 387d826..98fd1b0 100644 --- a/lib/model/addressmanager.js +++ b/lib/model/addressmanager.js @@ -55,6 +55,18 @@ AddressManager.prototype.rewindIndex = function(isChange, n) { } }; +AddressManager.prototype.getCurrentIndex = function(isChange) { + return isChange ? this.changeAddressIndex : this.receiveAddressIndex; +}; + + +AddressManager.prototype.getBaseAddressPath = function(isChange) { + return 'm/' + + (this.derivationStrategy == Constants.DERIVATION_STRATEGIES.BIP45 ? this.copayerIndex + '/' : '') + + (isChange ? 1 : 0) + '/' + + 0; +}; + AddressManager.prototype.getCurrentAddressPath = function(isChange) { return 'm/' + (this.derivationStrategy == Constants.DERIVATION_STRATEGIES.BIP45 ? this.copayerIndex + '/' : '') + diff --git a/lib/server.js b/lib/server.js index 2717402..6d683a9 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1037,6 +1037,9 @@ WalletService.prototype.createAddress = function(opts, cb) { self.getWallet({}, function(err, wallet) { if (err) return cb(err); if (!wallet.isComplete()) return cb(Errors.WALLET_NOT_COMPLETE); + if (wallet.scanStatus == 'error') + return cb(Errors.WALLET_NEED_SCAN); + var createFn = wallet.singleAddress ? getFirstAddress : createNewAddress; return createFn(wallet, cb); @@ -1161,6 +1164,10 @@ WalletService.prototype._getUtxosForCurrentWallet = function(opts, cb) { self.getWallet({}, function(err, wallet) { if (err) return next(err); + if (wallet.scanStatus == 'error') + return cb(Errors.WALLET_NEED_SCAN); + + coin = wallet.coin; return next(); }); @@ -2265,6 +2272,10 @@ WalletService.prototype.createTx = function(opts, cb) { if (err) return cb(err); if (!wallet.isComplete()) return cb(Errors.WALLET_NOT_COMPLETE); + if (wallet.scanStatus == 'error') + return cb(Errors.WALLET_NEED_SCAN); + + checkTxpAlreadyExists(opts.txProposalId, function(err, txp) { if (err) return cb(err); if (txp) return cb(null, txp); @@ -3330,23 +3341,31 @@ WalletService.prototype.scan = function(opts, cb) { var allAddresses = []; var gap = Defaults.SCAN_ADDRESS_GAP; + async.whilst(function() { - self.logi('Scanning addr gap:'+ inactiveCounter); + self.logi('Scanning addr branch: %s index: %d gap %d ', derivator.id, derivator.index(), inactiveCounter); return inactiveCounter < gap; }, function(next) { var address = derivator.derive(); + checkActivity(wallet, address.address, function(err, activity) { if (err) return next(err); allAddresses.push(address); inactiveCounter = activity ? 0 : inactiveCounter + 1; - next(); + + if (!activity) + return next(); + + // Store address manager cache. This is usefull for interrupted scans + // in which the addresses are not inserted. + self.storage.storeAddressIndexCache(wallet.id, derivator.id, derivator.index(),next); }); }, function(err) { derivator.rewind(gap); return cb(err, _.dropRight(allAddresses, gap)); }); - }; + } self._runLocked(cb, function(cb) { @@ -3363,14 +3382,18 @@ WalletService.prototype.scan = function(opts, cb) { var derivators = []; _.each([false, true], function(isChange) { derivators.push({ + id: wallet.addressManager.getBaseAddressPath(isChange), derive: _.bind(wallet.createAddress, wallet, isChange), + index: _.bind(wallet.addressManager.getCurrentIndex, wallet.addressManager, isChange), rewind: _.bind(wallet.addressManager.rewindIndex, wallet.addressManager, isChange), }); if (opts.includeCopayerBranches) { _.each(wallet.copayers, function(copayer) { if (copayer.addressManager) { derivators.push({ + id: copayer.addressManager.getBaseAddressPath(isChange), derive: _.bind(copayer.createAddress, copayer, wallet, isChange), + index: _.bind(copayer.addressManager.getCurrentIndex, copayer.addressManager, isChange), rewind: _.bind(copayer.addressManager.rewindIndex, copayer.addressManager, isChange), }); } @@ -3379,9 +3402,25 @@ WalletService.prototype.scan = function(opts, cb) { }); async.eachSeries(derivators, function(derivator, next) { - scanBranch(wallet, derivator, function(err, addresses) { - if (err) return next(err); - self.storage.storeAddressAndWallet(wallet, addresses, next); + + self.storage.fetchAddressIndexCache(wallet.id, derivator.id, function(err, index){ + var addresses = []; + + // prederive known addresses + var diff = index - derivator.index(); + if (diff>0) { + self.logi('Prederiving '+ diff +' addresses for ' + derivator.id); + for(var i =0; i Date: Fri, 9 Feb 2018 13:24:38 -0300 Subject: [PATCH 2/2] fix helper --- test/integration/helpers.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/test/integration/helpers.js b/test/integration/helpers.js index 9f26ad9..bb27232 100644 --- a/test/integration/helpers.js +++ b/test/integration/helpers.js @@ -385,12 +385,13 @@ var stubAddressActivityFailsOn = null; var stubAddressActivityFailsOnCount=1; helpers.stubAddressActivity = function(activeAddresses, failsOn) { - if (failsOn) { - stubAddressActivityFailsOn = failsOn; - } + stubAddressActivityFailsOnCount=1; + + // could be null + stubAddressActivityFailsOn = failsOn; blockchainExplorer.getAddressActivity = function(address, cb) { - if (stubAddressActivityFailsOnCount == stubAddressActivityFailsOn) + if (stubAddressActivityFailsOnCount === stubAddressActivityFailsOn) return cb('failed on request'); stubAddressActivityFailsOnCount++;