twallet: better handle used change addresses reverting to unused - electrum - Electrum Bitcoin wallet (HTM) git clone https://git.parazyd.org/electrum (DIR) Log (DIR) Files (DIR) Refs (DIR) Submodules --- (DIR) commit ac63444cfc7c7bca6c4b2a670da435e9b109269e (DIR) parent c14a704082d00f378c93b05cc5467be9e88991d2 (HTM) Author: SomberNight <somber.night@protonmail.com> Date: Mon, 25 May 2020 17:42:11 +0200 wallet: better handle used change addresses reverting to unused If a used/reserved change address became unused/unreserved, it would not get offered for usage by wallet until app restart. Make the used->unused transition less likely by requiring 3 confirmations (instead of considering even local/unconfirmed txs for 'used'); and avoid removing reserved addresses from the pool altogether. Diffstat: M electrum/wallet.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) --- (DIR) diff --git a/electrum/wallet.py b/electrum/wallet.py t@@ -381,15 +381,22 @@ class Abstract_Wallet(AddressSynchronizer, ABC): return addr return wrapper - def calc_unused_change_addresses(self): + def calc_unused_change_addresses(self) -> Sequence[str]: + """Returns a list of change addresses to choose from, for usage in e.g. new transactions. + The caller should give priority to earlier ones in the list. + """ with self.lock: - if hasattr(self, '_unused_change_addresses'): - addrs = self._unused_change_addresses - else: - addrs = self.get_change_addresses() - self._unused_change_addresses = [addr for addr in addrs - if not self.is_used(addr) and not self.is_address_reserved(addr)] - return list(self._unused_change_addresses) + # We want a list of unused change addresses. + # As a performance optimisation, to avoid checking all addresses every time, + # we maintain a list of "not old" addresses ("old" addresses have deeply confirmed history), + # and only check those. + if not hasattr(self, '_not_old_change_addresses'): + self._not_old_change_addresses = self.get_change_addresses() + self._not_old_change_addresses = [addr for addr in self._not_old_change_addresses + if not self.address_is_old(addr)] + unused_addrs = [addr for addr in self._not_old_change_addresses + if not self.is_used(addr) and not self.is_address_reserved(addr)] + return unused_addrs def is_deterministic(self) -> bool: return self.keystore.is_deterministic() t@@ -2294,8 +2301,8 @@ class Deterministic_Wallet(Abstract_Wallet): self.db.add_change_address(address) if for_change else self.db.add_receiving_address(address) self.add_address(address) if for_change: - # note: if it's actually used, it will get filtered later - self._unused_change_addresses.append(address) + # note: if it's actually "old", it will get filtered later + self._not_old_change_addresses.append(address) return address def synchronize_sequence(self, for_change):