mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-09-01 17:55:20 +00:00
kivy: implement opening storage-encrypted wallet files
This commit is contained in:
parent
72491bdf18
commit
02baae10d7
5 changed files with 59 additions and 23 deletions
|
@ -7,10 +7,10 @@ import traceback
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
import threading
|
import threading
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, Optional, Union, Callable
|
||||||
|
|
||||||
from electrum.storage import WalletStorage, StorageReadWriteError
|
from electrum.storage import WalletStorage, StorageReadWriteError
|
||||||
from electrum.wallet import Wallet, InternalAddressCorruption
|
from electrum.wallet import Wallet, InternalAddressCorruption, Abstract_Wallet
|
||||||
from electrum.plugin import run_hook
|
from electrum.plugin import run_hook
|
||||||
from electrum.util import (profiler, InvalidPassword, send_exception_to_crash_reporter,
|
from electrum.util import (profiler, InvalidPassword, send_exception_to_crash_reporter,
|
||||||
format_satoshis, format_satoshis_plain, format_fee_satoshis,
|
format_satoshis, format_satoshis_plain, format_fee_satoshis,
|
||||||
|
@ -81,7 +81,6 @@ from .uix.dialogs.lightning_channels import LightningChannelsDialog
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from . import ElectrumGui
|
from . import ElectrumGui
|
||||||
from electrum.simple_config import SimpleConfig
|
from electrum.simple_config import SimpleConfig
|
||||||
from electrum.wallet import Abstract_Wallet
|
|
||||||
from electrum.plugin import Plugins
|
from electrum.plugin import Plugins
|
||||||
|
|
||||||
|
|
||||||
|
@ -600,6 +599,16 @@ class ElectrumWindow(App):
|
||||||
self.load_wallet_by_name(self.electrum_config.get_wallet_path(use_gui_last_wallet=True),
|
self.load_wallet_by_name(self.electrum_config.get_wallet_path(use_gui_last_wallet=True),
|
||||||
ask_if_wizard=True)
|
ask_if_wizard=True)
|
||||||
|
|
||||||
|
def _on_decrypted_storage(self, storage: WalletStorage):
|
||||||
|
assert storage.is_past_initial_decryption()
|
||||||
|
if storage.requires_upgrade():
|
||||||
|
wizard = Factory.InstallWizard(self.electrum_config, self.plugins)
|
||||||
|
wizard.path = storage.path
|
||||||
|
wizard.bind(on_wizard_complete=self.on_wizard_complete)
|
||||||
|
wizard.upgrade_storage(storage)
|
||||||
|
else:
|
||||||
|
self.on_wizard_complete(wizard=None, storage=storage)
|
||||||
|
|
||||||
def load_wallet_by_name(self, path, ask_if_wizard=False):
|
def load_wallet_by_name(self, path, ask_if_wizard=False):
|
||||||
if not path:
|
if not path:
|
||||||
return
|
return
|
||||||
|
@ -608,23 +617,29 @@ class ElectrumWindow(App):
|
||||||
wallet = self.daemon.load_wallet(path, None)
|
wallet = self.daemon.load_wallet(path, None)
|
||||||
if wallet:
|
if wallet:
|
||||||
if platform == 'android' and wallet.has_password():
|
if platform == 'android' and wallet.has_password():
|
||||||
self.password_dialog(wallet, _('Enter PIN code'), lambda x: self.load_wallet(wallet), self.stop)
|
self.password_dialog(wallet=wallet, msg=_('Enter PIN code'),
|
||||||
|
on_success=lambda x: self.load_wallet(wallet), on_failure=self.stop)
|
||||||
else:
|
else:
|
||||||
self.load_wallet(wallet)
|
self.load_wallet(wallet)
|
||||||
else:
|
else:
|
||||||
def launch_wizard():
|
def launch_wizard():
|
||||||
wizard = Factory.InstallWizard(self.electrum_config, self.plugins)
|
|
||||||
wizard.path = path
|
|
||||||
wizard.bind(on_wizard_complete=self.on_wizard_complete)
|
|
||||||
storage = WalletStorage(path, manual_upgrades=True)
|
storage = WalletStorage(path, manual_upgrades=True)
|
||||||
if not storage.file_exists():
|
if not storage.file_exists():
|
||||||
|
wizard = Factory.InstallWizard(self.electrum_config, self.plugins)
|
||||||
|
wizard.path = path
|
||||||
|
wizard.bind(on_wizard_complete=self.on_wizard_complete)
|
||||||
wizard.run('new')
|
wizard.run('new')
|
||||||
elif storage.is_encrypted():
|
|
||||||
raise Exception("Kivy GUI does not support encrypted wallet files.")
|
|
||||||
elif storage.requires_upgrade():
|
|
||||||
wizard.upgrade_storage(storage)
|
|
||||||
else:
|
else:
|
||||||
raise Exception("unexpected storage file situation")
|
if storage.is_encrypted():
|
||||||
|
if not storage.is_encrypted_with_user_pw():
|
||||||
|
raise Exception("Kivy GUI does not support this type of encrypted wallet files.")
|
||||||
|
def on_password(pw):
|
||||||
|
storage.decrypt(pw)
|
||||||
|
self._on_decrypted_storage(storage)
|
||||||
|
self.password_dialog(wallet=storage, msg=_('Enter PIN code'),
|
||||||
|
on_success=on_password, on_failure=self.stop)
|
||||||
|
return
|
||||||
|
self._on_decrypted_storage(storage)
|
||||||
if not ask_if_wizard:
|
if not ask_if_wizard:
|
||||||
launch_wizard()
|
launch_wizard()
|
||||||
else:
|
else:
|
||||||
|
@ -917,7 +932,7 @@ class ElectrumWindow(App):
|
||||||
def on_resume(self):
|
def on_resume(self):
|
||||||
now = time.time()
|
now = time.time()
|
||||||
if self.wallet and self.wallet.has_password() and now - self.pause_time > 60:
|
if self.wallet and self.wallet.has_password() and now - self.pause_time > 60:
|
||||||
self.password_dialog(self.wallet, _('Enter PIN'), None, self.stop)
|
self.password_dialog(wallet=self.wallet, msg=_('Enter PIN'), on_success=None, on_failure=self.stop)
|
||||||
if self.nfcscanner:
|
if self.nfcscanner:
|
||||||
self.nfcscanner.nfc_enable()
|
self.nfcscanner.nfc_enable()
|
||||||
|
|
||||||
|
@ -1082,7 +1097,7 @@ class ElectrumWindow(App):
|
||||||
def protected(self, msg, f, args):
|
def protected(self, msg, f, args):
|
||||||
if self.wallet.has_password():
|
if self.wallet.has_password():
|
||||||
on_success = lambda pw: f(*(args + (pw,)))
|
on_success = lambda pw: f(*(args + (pw,)))
|
||||||
self.password_dialog(self.wallet, msg, on_success, lambda: None)
|
self.password_dialog(wallet=self.wallet, msg=msg, on_success=on_success, on_failure=lambda: None)
|
||||||
else:
|
else:
|
||||||
f(*(args + (None,)))
|
f(*(args + (None,)))
|
||||||
|
|
||||||
|
@ -1160,11 +1175,13 @@ class ElectrumWindow(App):
|
||||||
if passphrase:
|
if passphrase:
|
||||||
label.data += '\n\n' + _('Passphrase') + ': ' + passphrase
|
label.data += '\n\n' + _('Passphrase') + ': ' + passphrase
|
||||||
|
|
||||||
def password_dialog(self, wallet, msg, on_success, on_failure):
|
def password_dialog(self, *, wallet: Union[Abstract_Wallet, WalletStorage],
|
||||||
|
msg: str, on_success: Callable = None, on_failure: Callable = None):
|
||||||
from .uix.dialogs.password_dialog import PasswordDialog
|
from .uix.dialogs.password_dialog import PasswordDialog
|
||||||
if self._password_dialog is None:
|
if self._password_dialog is None:
|
||||||
self._password_dialog = PasswordDialog()
|
self._password_dialog = PasswordDialog()
|
||||||
self._password_dialog.init(self, wallet, msg, on_success, on_failure)
|
self._password_dialog.init(self, wallet=wallet, msg=msg,
|
||||||
|
on_success=on_success, on_failure=on_failure)
|
||||||
self._password_dialog.open()
|
self._password_dialog.open()
|
||||||
|
|
||||||
def change_password(self, cb):
|
def change_password(self, cb):
|
||||||
|
@ -1176,7 +1193,8 @@ class ElectrumWindow(App):
|
||||||
self.wallet.update_password(old_password, new_password)
|
self.wallet.update_password(old_password, new_password)
|
||||||
self.show_info(_("Your PIN code was updated"))
|
self.show_info(_("Your PIN code was updated"))
|
||||||
on_failure = lambda: self.show_error(_("PIN codes do not match"))
|
on_failure = lambda: self.show_error(_("PIN codes do not match"))
|
||||||
self._password_dialog.init(self, self.wallet, message, on_success, on_failure, is_change=1)
|
self._password_dialog.init(self, wallet=self.wallet, msg=message,
|
||||||
|
on_success=on_success, on_failure=on_failure, is_change=1)
|
||||||
self._password_dialog.open()
|
self._password_dialog.open()
|
||||||
|
|
||||||
def export_private_keys(self, pk_label, addr):
|
def export_private_keys(self, pk_label, addr):
|
||||||
|
|
|
@ -1157,7 +1157,8 @@ class InstallWizard(BaseWizard, Widget):
|
||||||
self.run('request_password', run_next)
|
self.run('request_password', run_next)
|
||||||
popup = PasswordDialog()
|
popup = PasswordDialog()
|
||||||
app = App.get_running_app()
|
app = App.get_running_app()
|
||||||
popup.init(app, None, _('Choose PIN code'), on_success, on_failure, is_change=2)
|
popup.init(app, wallet=None, msg=_('Choose PIN code'),
|
||||||
|
on_success=on_success, on_failure=on_failure, is_change=2)
|
||||||
popup.open()
|
popup.open()
|
||||||
|
|
||||||
def action_dialog(self, action, run_next):
|
def action_dialog(self, action, run_next):
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from typing import Callable, TYPE_CHECKING, Optional, Union
|
||||||
|
|
||||||
from kivy.app import App
|
from kivy.app import App
|
||||||
from kivy.factory import Factory
|
from kivy.factory import Factory
|
||||||
from kivy.properties import ObjectProperty
|
from kivy.properties import ObjectProperty
|
||||||
|
@ -8,6 +10,11 @@ from kivy.clock import Clock
|
||||||
from electrum.util import InvalidPassword
|
from electrum.util import InvalidPassword
|
||||||
from electrum.gui.kivy.i18n import _
|
from electrum.gui.kivy.i18n import _
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from ...main_window import ElectrumWindow
|
||||||
|
from electrum.wallet import Abstract_Wallet
|
||||||
|
from electrum.storage import WalletStorage
|
||||||
|
|
||||||
Builder.load_string('''
|
Builder.load_string('''
|
||||||
|
|
||||||
<PasswordDialog@Popup>
|
<PasswordDialog@Popup>
|
||||||
|
@ -71,10 +78,13 @@ Builder.load_string('''
|
||||||
|
|
||||||
class PasswordDialog(Factory.Popup):
|
class PasswordDialog(Factory.Popup):
|
||||||
|
|
||||||
def init(self, app, wallet, message, on_success, on_failure, is_change=0):
|
def init(self, app: 'ElectrumWindow', *,
|
||||||
|
wallet: Union['Abstract_Wallet', 'WalletStorage'] = None,
|
||||||
|
msg: str, on_success: Callable = None, on_failure: Callable = None,
|
||||||
|
is_change: int = 0):
|
||||||
self.app = app
|
self.app = app
|
||||||
self.wallet = wallet
|
self.wallet = wallet
|
||||||
self.message = message
|
self.message = msg
|
||||||
self.on_success = on_success
|
self.on_success = on_success
|
||||||
self.on_failure = on_failure
|
self.on_failure = on_failure
|
||||||
self.ids.kb.password = ''
|
self.ids.kb.password = ''
|
||||||
|
|
|
@ -203,7 +203,9 @@ class WalletStorage(Logger):
|
||||||
else:
|
else:
|
||||||
raise WalletFileException('no encryption magic for version: %s' % v)
|
raise WalletFileException('no encryption magic for version: %s' % v)
|
||||||
|
|
||||||
def decrypt(self, password):
|
def decrypt(self, password) -> None:
|
||||||
|
if self.is_past_initial_decryption():
|
||||||
|
return
|
||||||
ec_key = self.get_eckey_from_password(password)
|
ec_key = self.get_eckey_from_password(password)
|
||||||
if self.raw:
|
if self.raw:
|
||||||
enc_magic = self._get_encryption_magic()
|
enc_magic = self._get_encryption_magic()
|
||||||
|
@ -226,10 +228,12 @@ class WalletStorage(Logger):
|
||||||
s = s.decode('utf8')
|
s = s.decode('utf8')
|
||||||
return s
|
return s
|
||||||
|
|
||||||
def check_password(self, password):
|
def check_password(self, password) -> None:
|
||||||
"""Raises an InvalidPassword exception on invalid password"""
|
"""Raises an InvalidPassword exception on invalid password"""
|
||||||
if not self.is_encrypted():
|
if not self.is_encrypted():
|
||||||
return
|
return
|
||||||
|
if not self.is_past_initial_decryption():
|
||||||
|
self.decrypt(password) # this sets self.pubkey
|
||||||
if self.pubkey and self.pubkey != self.get_eckey_from_password(password).get_public_key_hex():
|
if self.pubkey and self.pubkey != self.get_eckey_from_password(password).get_public_key_hex():
|
||||||
raise InvalidPassword()
|
raise InvalidPassword()
|
||||||
|
|
||||||
|
@ -250,6 +254,9 @@ class WalletStorage(Logger):
|
||||||
# make sure next storage.write() saves changes
|
# make sure next storage.write() saves changes
|
||||||
self.db.set_modified(True)
|
self.db.set_modified(True)
|
||||||
|
|
||||||
|
def basename(self) -> str:
|
||||||
|
return os.path.basename(self.path)
|
||||||
|
|
||||||
def requires_upgrade(self):
|
def requires_upgrade(self):
|
||||||
if not self.is_past_initial_decryption():
|
if not self.is_past_initial_decryption():
|
||||||
raise Exception("storage not yet decrypted!")
|
raise Exception("storage not yet decrypted!")
|
||||||
|
|
|
@ -333,7 +333,7 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def basename(self) -> str:
|
def basename(self) -> str:
|
||||||
return os.path.basename(self.storage.path)
|
return self.storage.basename()
|
||||||
|
|
||||||
def test_addresses_sanity(self) -> None:
|
def test_addresses_sanity(self) -> None:
|
||||||
addrs = self.get_receiving_addresses()
|
addrs = self.get_receiving_addresses()
|
||||||
|
|
Loading…
Add table
Reference in a new issue