Browse Source

wallet backup function for kivy/android

master
ThomasV 6 years ago
parent
commit
87b7d2c0c0
  1. 15
      electrum/gui/kivy/main_window.py
  2. 2
      electrum/gui/kivy/tools/buildozer.spec
  3. 48
      electrum/gui/kivy/uix/dialogs/password_dialog.py
  4. 7
      electrum/gui/kivy/uix/ui_screens/status.kv
  5. 14
      electrum/util.py
  6. 14
      electrum/wallet.py

15
electrum/gui/kivy/main_window.py

@ -1199,6 +1199,21 @@ class ElectrumWindow(App):
on_success=on_success, on_failure=on_failure, is_change=1) on_success=on_success, on_failure=on_failure, is_change=1)
self._password_dialog.open() self._password_dialog.open()
def save_backup(self):
from .uix.dialogs.password_dialog import PasswordDialog
from electrum.util import get_backup_dir
if self._password_dialog is None:
self._password_dialog = PasswordDialog()
message = _("Create backup.") + '\n' + _("Enter your current PIN:")
def on_success(old_password, new_password):
new_path = os.path.join(get_backup_dir(self.electrum_config), self.wallet.basename() + '.backup')
self.wallet.save_backup(new_path, old_password=old_password, new_password=new_password)
self.show_info(_("Backup saved:") + f"\n{new_path}")
on_failure = lambda: self.show_error(_("PIN codes do not match"))
self._password_dialog.init(self, wallet=self.wallet, msg=message,
on_success=on_success, on_failure=on_failure, is_change=1, is_backup=True)
self._password_dialog.open()
def export_private_keys(self, pk_label, addr): def export_private_keys(self, pk_label, addr):
if self.wallet.is_watching_only(): if self.wallet.is_watching_only():
self.show_info(_('This is a watching-only wallet. It does not contain private keys.')) self.show_info(_('This is a watching-only wallet. It does not contain private keys.'))

2
electrum/gui/kivy/tools/buildozer.spec

@ -67,7 +67,7 @@ fullscreen = False
# #
# (list) Permissions # (list) Permissions
android.permissions = INTERNET, CAMERA android.permissions = INTERNET, CAMERA, WRITE_EXTERNAL_STORAGE
# (int) Android API to use # (int) Android API to use
android.api = 28 android.api = 28

48
electrum/gui/kivy/uix/dialogs/password_dialog.py

@ -19,6 +19,7 @@ Builder.load_string('''
<PasswordDialog@Popup> <PasswordDialog@Popup>
id: popup id: popup
is_generic: False
title: 'Electrum' title: 'Electrum'
message: '' message: ''
BoxLayout: BoxLayout:
@ -26,31 +27,17 @@ Builder.load_string('''
orientation: 'vertical' orientation: 'vertical'
Widget: Widget:
size_hint: 1, 0.05 size_hint: 1, 0.05
BoxLayout: Label:
size_hint: 1, None size_hint: 0.70, None
orientation: 'horizontal' font_size: '20dp'
Label: text: root.message
size_hint: 0.70, None text_size: self.width, None
font_size: '20dp'
text: root.message
text_size: self.width, None
Label:
size_hint: 0.23, None
font_size: '9dp'
text: _('Generic password')
CheckBox:
size_hint: 0.07, None
id: cb_generic_password
on_active:
box_generic_password.visible = self.active
kb.disabled = box_generic_password.visible
textinput_generic_password.focus = box_generic_password.visible
Widget: Widget:
size_hint: 1, 0.05 size_hint: 1, 0.05
BoxLayout: BoxLayout:
orientation: 'horizontal' orientation: 'horizontal'
id: box_generic_password id: box_generic_password
visible: False visible: root.is_generic
size_hint_y: 0.05 size_hint_y: 0.05
opacity: 1 if self.visible else 0 opacity: 1 if self.visible else 0
disabled: not self.visible disabled: not self.visible
@ -59,10 +46,11 @@ Builder.load_string('''
valign: 'center' valign: 'center'
multiline: False multiline: False
on_text_validate: on_text_validate:
popup.on_password(self.text, is_generic=True) popup.on_password(self.text)
password: True password: True
size_hint: 0.9, None size_hint: 0.9, None
unfocus_on_touch: False unfocus_on_touch: False
focus: root.is_generic
Button: Button:
size_hint: 0.1, None size_hint: 0.1, None
valign: 'center' valign: 'center'
@ -75,7 +63,7 @@ Builder.load_string('''
textinput_generic_password.password = False if textinput_generic_password.password else True textinput_generic_password.password = False if textinput_generic_password.password else True
Label: Label:
id: label_pin id: label_pin
visible: not box_generic_password.visible visible: not root.is_generic
size_hint_y: 0.05 size_hint_y: 0.05
opacity: 1 if self.visible else 0 opacity: 1 if self.visible else 0
disabled: not self.visible disabled: not self.visible
@ -86,6 +74,7 @@ Builder.load_string('''
size_hint: 1, 0.05 size_hint: 1, 0.05
GridLayout: GridLayout:
id: kb id: kb
disabled: root.is_generic
size_hint: 1, None size_hint: 1, None
height: self.minimum_height height: self.minimum_height
update_amount: popup.update_password update_amount: popup.update_password
@ -125,8 +114,9 @@ class PasswordDialog(Factory.Popup):
def init(self, app: 'ElectrumWindow', *, def init(self, app: 'ElectrumWindow', *,
wallet: Union['Abstract_Wallet', 'WalletStorage'] = None, wallet: Union['Abstract_Wallet', 'WalletStorage'] = None,
msg: str, on_success: Callable = None, on_failure: Callable = None, msg: str, on_success: Callable = None, on_failure: Callable = None,
is_change: int = 0): is_change: int = 0, is_backup: bool = False):
self.app = app self.app = app
self.is_backup = is_backup
self.wallet = wallet self.wallet = wallet
self.message = msg self.message = msg
self.on_success = on_success self.on_success = on_success
@ -138,7 +128,7 @@ class PasswordDialog(Factory.Popup):
self.pw = None self.pw = None
self.new_password = None self.new_password = None
self.title = 'Electrum' + (' - ' + self.wallet.basename() if self.wallet else '') self.title = 'Electrum' + (' - ' + self.wallet.basename() if self.wallet else '')
self.ids.cb_generic_password.active = False #self.ids.cb_generic_password.active = False
def check_password(self, password): def check_password(self, password):
if self.is_change > 1: if self.is_change > 1:
@ -172,8 +162,8 @@ class PasswordDialog(Factory.Popup):
text += c text += c
kb.password = text kb.password = text
def on_password(self, pw: str, *, is_generic=False): def on_password(self, pw: str):
if is_generic: if self.is_generic:
if len(pw) < 6: if len(pw) < 6:
self.app.show_error(_('Password is too short (min {} characters)').format(6)) self.app.show_error(_('Password is too short (min {} characters)').format(6))
return return
@ -186,18 +176,20 @@ class PasswordDialog(Factory.Popup):
self.dismiss() self.dismiss()
elif self.is_change == 1: elif self.is_change == 1:
self.pw = pw self.pw = pw
self.message = _('Enter new PIN') self.message = _('Enter a strong password for your backup') if self.is_backup else _('Enter new PIN')
self.ids.kb.password = '' self.ids.kb.password = ''
self.ids.textinput_generic_password.text = '' self.ids.textinput_generic_password.text = ''
self.is_change = 2 self.is_change = 2
self.is_generic = self.is_backup
elif self.is_change == 2: elif self.is_change == 2:
self.new_password = pw self.new_password = pw
self.message = _('Confirm new PIN') self.message = _('Confirm backup password') if self.is_backup else _('Confirm new PIN')
self.ids.kb.password = '' self.ids.kb.password = ''
self.ids.textinput_generic_password.text = '' self.ids.textinput_generic_password.text = ''
self.is_change = 3 self.is_change = 3
elif self.is_change == 3: elif self.is_change == 3:
self.success = pw == self.new_password self.success = pw == self.new_password
self.is_generic = False
self.dismiss() self.dismiss()
else: else:
self.app.show_error(_('Wrong PIN')) self.app.show_error(_('Wrong PIN'))

7
electrum/gui/kivy/uix/ui_screens/status.kv

@ -83,13 +83,14 @@ Popup:
Button: Button:
size_hint: 0.5, None size_hint: 0.5, None
height: '48dp' height: '48dp'
text: _('Disable LN') if app.wallet.has_lightning() else _('Enable LN') text: _('Save Backup')
on_release: on_release:
root.dismiss() root.dismiss()
app.toggle_lightning() app.save_backup()
Button: Button:
size_hint: 0.5, None size_hint: 0.5, None
height: '48dp' height: '48dp'
text: _('Close') text: _('Disable LN') if app.wallet.has_lightning() else _('Enable LN')
on_release: on_release:
root.dismiss() root.dismiss()
app.toggle_lightning()

14
electrum/util.py

@ -425,11 +425,25 @@ def profiler(func):
return lambda *args, **kw_args: do_profile(args, kw_args) return lambda *args, **kw_args: do_profile(args, kw_args)
def android_ext_dir():
import jnius
env = jnius.autoclass('android.os.Environment')
return env.getExternalStorageDirectory().getPath()
def android_backup_dir():
d = android_ext_dir() + '/org.electrum.electrum'
if not os.path.exists(d):
os.mkdir(d)
return d
def android_data_dir(): def android_data_dir():
import jnius import jnius
PythonActivity = jnius.autoclass('org.kivy.android.PythonActivity') PythonActivity = jnius.autoclass('org.kivy.android.PythonActivity')
return PythonActivity.mActivity.getFilesDir().getPath() + '/data' return PythonActivity.mActivity.getFilesDir().getPath() + '/data'
def get_backup_dir(config):
return android_backup_dir() if 'ANDROID_DATA' in os.environ else config.path
def ensure_sparse_file(filename): def ensure_sparse_file(filename):
# On modern Linux, no need to do anything. # On modern Linux, no need to do anything.

14
electrum/wallet.py

@ -263,12 +263,16 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
if self.storage: if self.storage:
self.db.write(self.storage) self.db.write(self.storage)
def save_backup(self, path): def save_backup(self, path, *, old_password=None, new_password=None):
# fixme: we need to change password... new_db = WalletDB(self.storage.read(), manual_upgrades=False)
new_db.put('is_backup', True)
new_storage = WalletStorage(path) new_storage = WalletStorage(path)
self.db.put('is_backup', True) #new_storage._encryption_version = self.storage.get_encryption_version()
self.db.write(new_storage) new_storage._encryption_version = StorageEncryptionVersion.PLAINTEXT
self.db.put('is_backup', None) w2 = Wallet(new_db, new_storage, config=self.config)
if new_password:
w2.update_password(old_password, new_password, encrypt_storage=True)
w2.save_db()
def has_lightning(self): def has_lightning(self):
return bool(self.lnworker) return bool(self.lnworker)

Loading…
Cancel
Save