From 73ba00d7dd87af5e63ab2a30617fae1b8d36d0c4 Mon Sep 17 00:00:00 2001 From: SomberNight Date: Mon, 8 Aug 2022 16:34:04 +0200 Subject: [PATCH] wallet.restore_wallet_from_text: support creating wallet in-memory --- electrum/storage.py | 8 ++++---- electrum/tests/test_wallet.py | 13 +++++++++++++ electrum/wallet.py | 24 ++++++++++++++---------- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/electrum/storage.py b/electrum/storage.py index e80fd1ca3..1f17a92a0 100644 --- a/electrum/storage.py +++ b/electrum/storage.py @@ -102,7 +102,7 @@ class WalletStorage(Logger): def file_exists(self) -> bool: return self._file_exists - def is_past_initial_decryption(self): + def is_past_initial_decryption(self) -> bool: """Return if storage is in a usable state for normal operations. The value is True exactly @@ -111,14 +111,14 @@ class WalletStorage(Logger): """ return not self.is_encrypted() or bool(self.pubkey) - def is_encrypted(self): + def is_encrypted(self) -> bool: """Return if storage encryption is currently enabled.""" return self.get_encryption_version() != StorageEncryptionVersion.PLAINTEXT - def is_encrypted_with_user_pw(self): + def is_encrypted_with_user_pw(self) -> bool: return self.get_encryption_version() == StorageEncryptionVersion.USER_PASSWORD - def is_encrypted_with_hw_device(self): + def is_encrypted_with_hw_device(self) -> bool: return self.get_encryption_version() == StorageEncryptionVersion.XPUB_PASSWORD def get_encryption_version(self): diff --git a/electrum/tests/test_wallet.py b/electrum/tests/test_wallet.py index 3051d39d6..c252dc5b7 100644 --- a/electrum/tests/test_wallet.py +++ b/electrum/tests/test_wallet.py @@ -195,6 +195,19 @@ class TestCreateRestoreWallet(WalletTestCase): self.assertEqual(encrypt_file, wallet.storage.is_encrypted()) self.assertEqual('bc1q2ccr34wzep58d4239tl3x3734ttle92a8srmuw', wallet.get_receiving_addresses()[0]) + def test_restore_wallet_from_text_no_storage(self): + text = 'bitter grass shiver impose acquire brush forget axis eager alone wine silver' + d = restore_wallet_from_text( + text, + path=None, + gap_limit=1, + config=self.config, + ) + wallet = d['wallet'] # type: Standard_Wallet + self.assertEqual(None, wallet.storage) + self.assertEqual(text, wallet.keystore.get_seed(None)) + self.assertEqual('bc1q3g5tmkmlvxryhh843v4dz026avatc0zzr6h3af', wallet.get_receiving_addresses()[0]) + def test_restore_wallet_from_text_xpub(self): text = 'zpub6nydoME6CFdJtMpzHW5BNoPz6i6XbeT9qfz72wsRqGdgGEYeivso6xjfw8cGcCyHwF7BNW4LDuHF35XrZsovBLWMF4qXSjmhTXYiHbWqGLt' d = restore_wallet_from_text(text, path=self.wallet_path, gap_limit=1, config=self.config) diff --git a/electrum/wallet.py b/electrum/wallet.py index 4534b2e15..52fcec233 100644 --- a/electrum/wallet.py +++ b/electrum/wallet.py @@ -2561,7 +2561,7 @@ class Abstract_Wallet(ABC, Logger, EventListener): def can_delete_address(self): return False - def has_password(self): + def has_password(self) -> bool: return self.has_keystore_encryption() or self.has_storage_encryption() def can_have_keystore_encryption(self): @@ -2578,18 +2578,18 @@ class Abstract_Wallet(ABC, Logger, EventListener): else: return StorageEncryptionVersion.USER_PASSWORD - def has_keystore_encryption(self): + def has_keystore_encryption(self) -> bool: """Returns whether encryption is enabled for the keystore. If True, e.g. signing a transaction will require a password. """ if self.can_have_keystore_encryption(): - return self.db.get('use_encryption', False) + return bool(self.db.get('use_encryption', False)) return False - def has_storage_encryption(self): + def has_storage_encryption(self) -> bool: """Returns whether encryption is enabled for the wallet file on disk.""" - return self.storage and self.storage.is_encrypted() + return bool(self.storage) and self.storage.is_encrypted() @classmethod def may_have_password(cls): @@ -3484,15 +3484,18 @@ def create_new_wallet(*, path, config: SimpleConfig, passphrase=None, password=N return {'seed': seed, 'wallet': wallet, 'msg': msg} -def restore_wallet_from_text(text, *, path, config: SimpleConfig, +def restore_wallet_from_text(text, *, path: Optional[str], config: SimpleConfig, passphrase=None, password=None, encrypt_file=True, gap_limit=None) -> dict: """Restore a wallet from text. Text can be a seed phrase, a master public key, a master private key, a list of bitcoin addresses or bitcoin private keys.""" - storage = WalletStorage(path) - if storage.file_exists(): - raise Exception("Remove the existing wallet first!") + if path is None: # create wallet in-memory + storage = None + else: + storage = WalletStorage(path) + if storage.file_exists(): + raise Exception("Remove the existing wallet first!") db = WalletDB('', manual_upgrades=False) text = text.strip() if keystore.is_address_list(text): @@ -3525,7 +3528,8 @@ def restore_wallet_from_text(text, *, path, config: SimpleConfig, if gap_limit is not None: db.put('gap_limit', gap_limit) wallet = Wallet(db, storage, config=config) - assert not storage.file_exists(), "file was created too soon! plaintext keys might have been written to disk" + if storage: + assert not storage.file_exists(), "file was created too soon! plaintext keys might have been written to disk" wallet.update_password(old_pw=None, new_pw=password, encrypt_storage=encrypt_file) wallet.synchronize() msg = ("This wallet was restored offline. It may contain more addresses than displayed. "