From 4e80ef818df0295fa46ee50bbe695b7e6eab1eda Mon Sep 17 00:00:00 2001 From: ThomasV Date: Wed, 13 Sep 2023 18:10:23 +0200 Subject: [PATCH] Add unlock command to CLI (stores wallet password in memory) --- electrum/commands.py | 12 ++++++++++++ electrum/wallet.py | 12 ++++++++++++ run_electrum | 3 ++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/electrum/commands.py b/electrum/commands.py index 326f86a1e..09ceea929 100644 --- a/electrum/commands.py +++ b/electrum/commands.py @@ -146,6 +146,12 @@ def command(s): if wallet is None: raise Exception('wallet not loaded') kwargs['wallet'] = wallet + if cmd.requires_password and password is None: + password = wallet.get_unlocked_password() + if password: + kwargs['password'] = password + else: + raise Exception('Password required. Unlock the wallet, or add a --password option to your command') wallet = kwargs.get('wallet') # type: Optional[Abstract_Wallet] if cmd.requires_wallet and not wallet: raise Exception('wallet not loaded') @@ -621,6 +627,12 @@ class Commands: return ret + @command('w') + async def unlock(self, password=None, wallet: Abstract_Wallet = None): + """Unlock the wallet. The wallet password will be stored in memory""" + wallet.unlock(password) + return "wallet unlocked" if password else "wallet locked" + @command('w') async def getmpk(self, wallet: Abstract_Wallet = None): """Get master public key. Return your wallet\'s master public key""" diff --git a/electrum/wallet.py b/electrum/wallet.py index 61f9762a3..0c14a2c08 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -318,6 +318,7 @@ class Abstract_Wallet(ABC, Logger, EventListener): # load addresses needs to be called before constructor for sanity checks db.load_addresses(self.wallet_type) self.keystore = None # type: Optional[KeyStore] # will be set by load_keystore + self._password_in_memory = None # see self.unlock Logger.__init__(self) self.network = None @@ -2786,6 +2787,7 @@ class Abstract_Wallet(ABC, Logger, EventListener): encrypt_keystore = self.can_have_keystore_encryption() self.db.set_keystore_encryption(bool(new_pw) and encrypt_keystore) self.save_db() + self.unlock(None) @abstractmethod def _update_password_for_keystore(self, old_pw: Optional[str], new_pw: Optional[str]) -> None: @@ -3035,6 +3037,16 @@ class Abstract_Wallet(ABC, Logger, EventListener): """Returns the number of new addresses we generated.""" return 0 + def unlock(self, password): + self.logger.info(f'unlocking wallet') + if password: + self.check_password(password) + self._password_in_memory = password + + def get_unlocked_password(self): + return self._password_in_memory + + class Simple_Wallet(Abstract_Wallet): # wallet with a single keystore diff --git a/run_electrum b/run_electrum index e8a965f40..274593631 100755 --- a/run_electrum +++ b/run_electrum @@ -155,7 +155,8 @@ def init_cmdline(config_options, wallet_path, *, rpcserver: bool, config: 'Simpl # commands needing password if ((cmd.requires_wallet and storage.is_encrypted() and not rpcserver) or (cmdname == 'load_wallet' and storage.is_encrypted()) - or cmd.requires_password): + or (cmdname in ['password', 'unlock']) + or (cmd.requires_password and not rpcserver)): if storage.is_encrypted_with_hw_device(): # this case is handled later in the control flow password = None