diff --git a/lib/model/addressmanager.js b/lib/model/addressmanager.js index bca94f6..6d9d66f 100644 --- a/lib/model/addressmanager.js +++ b/lib/model/addressmanager.js @@ -38,6 +38,15 @@ AddressManager.prototype._incrementIndex = function(isChange) { } }; +AddressManager.prototype.rewindIndex = function(isChange, n) { + n = _.isUndefined(n) ? 1 : n; + if (isChange) { + this.changeAddressIndex = Math.max(0, this.changeAddressIndex - n); + } else { + this.receiveAddressIndex = Math.max(0, this.receiveAddressIndex - n); + } +}; + AddressManager.prototype.getCurrentAddressPath = function(isChange) { return 'm/' + this.copayerIndex + '/' + diff --git a/lib/server.js b/lib/server.js index cf9bc16..0125b63 100644 --- a/lib/server.js +++ b/lib/server.js @@ -432,9 +432,9 @@ WalletService.prototype.verifyMessageSignature = function(opts, cb) { WalletService.prototype._getBlockchainExplorer = function(provider, network) { if (!this.blockchainExplorer) { - var opts = {}; + var opts = {}; if (this.blockchainExplorerOpts && this.blockchainExplorerOpts[network]) { - opts = this.blockchainExplorerOpts[network]; + opts = this.blockchainExplorerOpts[network]; } opts.provider = provider; opts.network = network; @@ -1164,7 +1164,7 @@ WalletService.prototype.scan = function(opts, cb) { function deriveAddresses(size, derivator, cb) { async.mapSeries(_.range(size), function(i, next) { setTimeout(function() { - next(null, derivator()); + next(null, derivator.derive()); }, WalletService.scanConfig.DERIVATION_DELAY) }, cb); }; @@ -1184,10 +1184,14 @@ WalletService.prototype.scan = function(opts, cb) { deriveAddresses(WalletService.scanConfig.SCAN_WINDOW, derivator, function(err, addresses) { if (err) return next(err); networkName = networkName || Bitcore.Address(addresses[0].address).toObject().network; - allAddresses.push(addresses); checkActivity(_.pluck(addresses, 'address'), networkName, function(err, thereIsActivity) { if (err) return next(new Error('Could not fetch TX activity:' + err)); activity = thereIsActivity; + if (thereIsActivity) { + allAddresses.push(addresses); + } else { + derivator.rewind(WalletService.scanConfig.SCAN_WINDOW); + } next(); }); }); @@ -1208,10 +1212,16 @@ WalletService.prototype.scan = function(opts, cb) { var derivators = []; _.each([false, true], function(isChange) { - derivators.push(_.bind(wallet.createAddress, wallet, isChange)); + derivators.push({ + derive: _.bind(wallet.createAddress, wallet, isChange), + rewind: _.bind(wallet.addressManager.rewindIndex, wallet.addressManager, isChange), + }); if (opts.includeCopayerBranches) { _.each(wallet.copayers, function(copayer) { - derivators.push(_.bind(copayer.createAddress, copayer, wallet, isChange)); + derivators.push({ + derive: _.bind(copayer.createAddress, copayer, wallet, isChange), + rewind: _.bind(copayer.addressManager.rewindIndex, copayer.addressManager, isChange), + }); }); } }); diff --git a/test/integration/server.js b/test/integration/server.js index 0db5f9e..9e3c728 100644 --- a/test/integration/server.js +++ b/test/integration/server.js @@ -2538,7 +2538,11 @@ describe('Wallet service', function() { }); it('should scan main addresses', function(done) { - helpers.stubAddressActivity(['3K2VWMXheGZ4qG35DyGjA2dLeKfaSr534A']); + helpers.stubAddressActivity( + ['3K2VWMXheGZ4qG35DyGjA2dLeKfaSr534A', // m/2147483647/0/0 + '3NezgtNbuDzL2sFhnfxyVy8bHp4v6ud252', // m/2147483647/0/2 + '3CQ2hCMUu1SCPVPMpfCCuT3nAfHGiHV1o7', // m/2147483647/1/0 + ]); var expectedPaths = [ 'm/2147483647/0/0', 'm/2147483647/0/1', @@ -2563,18 +2567,20 @@ describe('Wallet service', function() { }); }); it('should scan main addresses & copayer addresses', function(done) { - helpers.stubAddressActivity(['3K2VWMXheGZ4qG35DyGjA2dLeKfaSr534A']); + helpers.stubAddressActivity( + ['3K2VWMXheGZ4qG35DyGjA2dLeKfaSr534A', // m/2147483647/0/0 + '3CQ2hCMUu1SCPVPMpfCCuT3nAfHGiHV1o7', // m/2147483647/1/0 + '3BYHznBmosYxUj1NWcjdFKX2tdsH7UT1YG', // m/0/0/1 + '3Eg1uPkGnwyU42bRiaDuo6Cu9bjjhoG7Sh', // m/1/1/0 + '3AYmZ63tMd2AHN8QLfu5D2nfRzCH66psWx', // m/1/0/0 + ]); var expectedPaths = [ 'm/2147483647/0/0', 'm/2147483647/0/1', - 'm/2147483647/0/2', - 'm/2147483647/0/3', 'm/2147483647/1/0', 'm/2147483647/1/1', 'm/0/0/0', 'm/0/0/1', - 'm/0/1/0', - 'm/0/1/1', 'm/1/0/0', 'm/1/0/1', 'm/1/1/0', @@ -2658,7 +2664,11 @@ describe('Wallet service', function() { }); it('should start an asynchronous scan', function(done) { - helpers.stubAddressActivity(['3K2VWMXheGZ4qG35DyGjA2dLeKfaSr534A']); + helpers.stubAddressActivity([ + '3K2VWMXheGZ4qG35DyGjA2dLeKfaSr534A', // m/2147483647/0/0 + '3NezgtNbuDzL2sFhnfxyVy8bHp4v6ud252', // m/2147483647/0/2 + '3CQ2hCMUu1SCPVPMpfCCuT3nAfHGiHV1o7', // m/2147483647/1/1 + ]); var expectedPaths = [ 'm/2147483647/0/0', 'm/2147483647/0/1', diff --git a/test/models/addressmanager.js b/test/models/addressmanager.js index 31236d3..eea0286 100644 --- a/test/models/addressmanager.js +++ b/test/models/addressmanager.js @@ -33,4 +33,28 @@ describe('AddressManager', function() { am.getNewAddressPath(true).should.equal('m/2/1/0'); }); }); + describe('#rewindIndex', function() { + it('should rewind main index', function() { + var am = AddressManager.create({}); + am.getNewAddressPath(false).should.equal('m/2147483647/0/0'); + am.getNewAddressPath(false).should.equal('m/2147483647/0/1'); + am.getNewAddressPath(false).should.equal('m/2147483647/0/2'); + am.rewindIndex(false, 2); + am.getNewAddressPath(false).should.equal('m/2147483647/0/1'); + }); + it('should rewind change index', function() { + var am = AddressManager.create({}); + am.getNewAddressPath(true).should.equal('m/2147483647/1/0'); + am.rewindIndex(false, 1); + am.getNewAddressPath(true).should.equal('m/2147483647/1/1'); + am.rewindIndex(true, 2); + am.getNewAddressPath(true).should.equal('m/2147483647/1/0'); + }); + it('should stop at 0', function() { + var am = AddressManager.create({}); + am.getNewAddressPath(false).should.equal('m/2147483647/0/0'); + am.rewindIndex(false, 20); + am.getNewAddressPath(false).should.equal('m/2147483647/0/0'); + }); + }); });