2929import re
3030import copy
3131from typing import Tuple , TYPE_CHECKING , Union , Sequence , Optional , Dict , List , NamedTuple , Any , Type
32- from functools import lru_cache , wraps
32+ from functools import wraps
3333from abc import ABC , abstractmethod
3434
3535import electrum_ecc as ecc
5252from .mnemonic import Mnemonic , Wordlist , calc_seed_type , is_seed
5353from .plugin import run_hook
5454from .logging import Logger
55+ from .lrucache import LRUCache
5556
5657if TYPE_CHECKING :
5758 from .gui .common_qt .util import TaskThread
@@ -398,6 +399,9 @@ def get_passphrase(self, password) -> str:
398399
399400class MasterPublicKeyMixin (ABC ):
400401
402+ def __init__ (self ):
403+ self ._pubkey_cache = LRUCache (maxsize = 10 ** 4 ) # type: LRUCache[Sequence[int], bytes] # path->pubkey
404+
401405 @abstractmethod
402406 def get_master_public_key (self ) -> str :
403407 pass
@@ -434,8 +438,14 @@ def get_fp_and_derivation_to_be_used_in_partial_tx(
434438 def get_key_origin_info (self ) -> Optional [KeyOriginInfo ]:
435439 return None
436440
437- @abstractmethod
438441 def derive_pubkey (self , for_change : int , n : int ) -> bytes :
442+ key = (for_change , n )
443+ if key not in self ._pubkey_cache :
444+ self ._pubkey_cache [key ] = self ._derive_pubkey (* key )
445+ return self ._pubkey_cache [key ]
446+
447+ @abstractmethod
448+ def _derive_pubkey (self , for_change : int , n : int ) -> bytes :
439449 """Returns pubkey at given path.
440450 May raise CannotDerivePubkey.
441451 """
@@ -501,6 +511,7 @@ def test_der_suffix_against_pubkey(der_suffix: Sequence[int], pubkey: bytes) ->
501511class Xpub (MasterPublicKeyMixin ):
502512
503513 def __init__ (self , * , derivation_prefix : str = None , root_fingerprint : str = None ):
514+ MasterPublicKeyMixin .__init__ (self )
504515 self .xpub = None
505516 self .xpub_receive = None
506517 self .xpub_change = None
@@ -608,8 +619,7 @@ def add_key_origin(self, *, derivation_prefix: str = None, root_fingerprint: str
608619 self ._derivation_prefix = derivation_prefix
609620 self .is_requesting_to_be_rewritten_to_wallet_file = True
610621
611- @lru_cache (maxsize = None )
612- def derive_pubkey (self , for_change : int , n : int ) -> bytes :
622+ def _derive_pubkey (self , for_change : int , n : int ) -> bytes :
613623 for_change = int (for_change )
614624 if for_change not in (0 , 1 ):
615625 raise CannotDerivePubkey ("forbidden path" )
@@ -624,7 +634,7 @@ def derive_pubkey(self, for_change: int, n: int) -> bytes:
624634 return self .get_pubkey_from_xpub (xpub , (n ,))
625635
626636 @classmethod
627- def get_pubkey_from_xpub (self , xpub : str , sequence ) -> bytes :
637+ def get_pubkey_from_xpub (cls , xpub : str , sequence ) -> bytes :
628638 node = BIP32Node .from_xkey (xpub ).subkey_at_public_derivation (sequence )
629639 return node .eckey .get_public_key_bytes (compressed = True )
630640
@@ -729,6 +739,7 @@ class Old_KeyStore(MasterPublicKeyMixin, Deterministic_KeyStore):
729739 type = 'old'
730740
731741 def __init__ (self , d : dict ):
742+ MasterPublicKeyMixin .__init__ (self )
732743 Deterministic_KeyStore .__init__ (self , d )
733744 self .mpk = d .get ('mpk' ) # type: Optional[str]
734745 self ._root_fingerprint = None
@@ -807,8 +818,7 @@ def get_pubkey_from_mpk(cls, mpk: str, for_change: int, n: int) -> bytes:
807818 public_key = master_public_key + z * ecc .GENERATOR
808819 return public_key .get_public_key_bytes (compressed = False )
809820
810- @lru_cache (maxsize = None )
811- def derive_pubkey (self , for_change , n ) -> bytes :
821+ def _derive_pubkey (self , for_change , n ) -> bytes :
812822 for_change = int (for_change )
813823 if for_change not in (0 , 1 ):
814824 raise CannotDerivePubkey ("forbidden path" )
0 commit comments