From 4468e3618313b9e250cfb05db5a45169b3385596 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 31 Mar 2020 06:24:43 -0700 Subject: [PATCH] wallet: add migration to regenerate missing change addresses. see #414, #413, #411. --- lib/wallet/wallet.js | 6 ++++++ lib/wallet/walletdb.js | 45 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/lib/wallet/wallet.js b/lib/wallet/wallet.js index e6d0667b69..29994dc3a2 100644 --- a/lib/wallet/wallet.js +++ b/lib/wallet/wallet.js @@ -72,6 +72,7 @@ class Wallet extends EventEmitter { this.wid = 0; this.id = null; this.watchOnly = false; + this.fixedChange = false; this.accountDepth = 0; this.token = consensus.ZERO_HASH; this.tokenDepth = 0; @@ -4456,6 +4457,7 @@ class Wallet extends EventEmitter { wid: this.wid, id: this.id, watchOnly: this.watchOnly, + fixedChange: this.fixedChange, accountDepth: this.accountDepth, token: this.token.toString('hex'), tokenDepth: this.tokenDepth, @@ -4502,6 +4504,9 @@ class Wallet extends EventEmitter { if (this.watchOnly) flags |= 1; + if (this.fixedChange) + flags |= 2; + bw.writeU8(flags); bw.writeU32(this.accountDepth); bw.writeBytes(this.token); @@ -4523,6 +4528,7 @@ class Wallet extends EventEmitter { const flags = br.readU8(); this.watchOnly = (flags & 1) !== 0; + this.fixedChange = (flags & 2) !== 0; this.accountDepth = br.readU32(); this.token = br.readBytes(32); this.tokenDepth = br.readU32(); diff --git a/lib/wallet/walletdb.js b/lib/wallet/walletdb.js index 746ff86799..e9af3939ad 100644 --- a/lib/wallet/walletdb.js +++ b/lib/wallet/walletdb.js @@ -802,6 +802,9 @@ class WalletDB extends EventEmitter { this.register(wallet); + if (!wallet.fixedChange) + await this._migrateChange(wallet); + return wallet; } @@ -1087,6 +1090,7 @@ class WalletDB extends EventEmitter { const wallet = Wallet.fromOptions(this, options); wallet.wid = this.depth; + wallet.fixedChange = true; await wallet.init(options, options.passphrase); @@ -2270,6 +2274,47 @@ class WalletDB extends EventEmitter { return this.rollback(entry.height); } + + /** + * Run change address migration. + * @param {Wallet} wallet + */ + + async _migrateChange(wallet) { + const b = this.db.batch(); + + this.logger.info('Regenerating change addresses.'); + + let total = 0; + + for (let i = 0; i < wallet.accountDepth; i++) { + const account = await wallet.getAccount(i); + + for (let i = 0; i < account.changeDepth + account.lookahead; i++) { + const key = account.deriveChange(i); + const path = key.toPath(); + + if (!await this.hasPath(account.wid, path.hash)) { + await this.savePath(b, account.wid, path); + total += 1; + } + } + } + + wallet.fixedChange = true; + + this.save(b, wallet); + + await b.write(); + + if (total > 0) { + this.logger.warning('Regenerated %d change addresses.', total); + this.logger.warning( + 'Please rescan if you believe you have missing outputs.'); + } else { + this.logger.info('All change addresses present.'); + } + } } /**