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):