5 changed files with 850 additions and 829 deletions
@ -1,623 +0,0 @@
|
||||
|
||||
from functools import partial |
||||
|
||||
from kivy.app import App |
||||
from kivy.clock import Clock |
||||
from kivy.lang import Builder |
||||
from kivy.properties import ObjectProperty, StringProperty, OptionProperty |
||||
from kivy.core.window import Window |
||||
from kivy.uix.button import Button |
||||
from kivy.utils import platform |
||||
|
||||
from electrum_gui.kivy.uix.dialogs import EventsDialog |
||||
from electrum_gui.kivy.i18n import _ |
||||
|
||||
test_xpub = "xpub661MyMwAqRbcFpV2JqonBDKdgJiExpxiSAtvphtpviunv42FNVJNNRA3Zdy5kQXoK7NpwUC2QQPXVMLKLoHxaekNfemFs5zkfrNnk91dobZ" |
||||
test_seed = "powder idea leader task pretty harsh resemble alert quit athlete clerk almost able" |
||||
is_test = platform != 'android' |
||||
|
||||
Builder.load_string(''' |
||||
#:import Window kivy.core.window.Window |
||||
#:import _ electrum_gui.kivy.i18n._ |
||||
|
||||
|
||||
<WizardTextInput@TextInput> |
||||
border: 4, 4, 4, 4 |
||||
font_size: '15sp' |
||||
padding: '15dp', '15dp' |
||||
background_color: (1, 1, 1, 1) if self.focus else (0.454, 0.698, 0.909, 1) |
||||
foreground_color: (0.31, 0.31, 0.31, 1) if self.focus else (0.835, 0.909, 0.972, 1) |
||||
hint_text_color: self.foreground_color |
||||
background_active: 'atlas://gui/kivy/theming/light/create_act_text_active' |
||||
background_normal: 'atlas://gui/kivy/theming/light/create_act_text_active' |
||||
size_hint_y: None |
||||
height: '48sp' |
||||
|
||||
<WizardButton@Button>: |
||||
root: None |
||||
size_hint: 1, None |
||||
height: '48sp' |
||||
on_press: if self.root: self.root.dispatch('on_press', self) |
||||
on_release: if self.root: self.root.dispatch('on_release', self) |
||||
|
||||
<BigLabel@Label> |
||||
color: .854, .925, .984, 1 |
||||
size_hint: 1, None |
||||
text_size: self.width, None |
||||
height: self.texture_size[1] |
||||
bold: True |
||||
|
||||
<-WizardDialog> |
||||
text_color: .854, .925, .984, 1 |
||||
value: '' |
||||
#auto_dismiss: False |
||||
size_hint: None, None |
||||
canvas.before: |
||||
Color: |
||||
rgba: 0, 0, 0, .9 |
||||
Rectangle: |
||||
size: Window.size |
||||
Color: |
||||
rgba: .239, .588, .882, 1 |
||||
Rectangle: |
||||
size: Window.size |
||||
|
||||
crcontent: crcontent |
||||
# add electrum icon |
||||
BoxLayout: |
||||
orientation: 'vertical' if self.width < self.height else 'horizontal' |
||||
padding: |
||||
min(dp(27), self.width/32), min(dp(27), self.height/32),\ |
||||
min(dp(27), self.width/32), min(dp(27), self.height/32) |
||||
spacing: '10dp' |
||||
GridLayout: |
||||
id: grid_logo |
||||
cols: 1 |
||||
pos_hint: {'center_y': .5} |
||||
size_hint: 1, None |
||||
height: self.minimum_height |
||||
Label: |
||||
color: root.text_color |
||||
text: 'ELECTRUM' |
||||
size_hint: 1, None |
||||
height: self.texture_size[1] if self.opacity else 0 |
||||
font_size: '33sp' |
||||
font_name: 'gui/kivy/data/fonts/tron/Tr2n.ttf' |
||||
GridLayout: |
||||
cols: 1 |
||||
id: crcontent |
||||
spacing: '1dp' |
||||
Widget: |
||||
size_hint: 1, 0.3 |
||||
GridLayout: |
||||
rows: 1 |
||||
spacing: '12dp' |
||||
size_hint: 1, None |
||||
height: self.minimum_height |
||||
WizardButton: |
||||
id: back |
||||
text: _('Back') |
||||
root: root |
||||
WizardButton: |
||||
id: next |
||||
text: _('Next') |
||||
root: root |
||||
disabled: root.value == '' |
||||
|
||||
|
||||
<WizardMultisigDialog> |
||||
value: 'next' |
||||
Widget |
||||
size_hint: 1, 1 |
||||
Label: |
||||
color: root.text_color |
||||
size_hint: 1, None |
||||
text_size: self.width, None |
||||
height: self.texture_size[1] |
||||
text: _("Choose the number of signatures needed to unlock funds in your wallet") |
||||
Widget |
||||
size_hint: 1, 1 |
||||
GridLayout: |
||||
orientation: 'vertical' |
||||
cols: 2 |
||||
spacing: '14dp' |
||||
size_hint: 1, 1 |
||||
height: self.minimum_height |
||||
Label: |
||||
color: root.text_color |
||||
text: _('From %d cosigners')%n.value |
||||
Slider: |
||||
id: n |
||||
range: 2, 5 |
||||
step: 1 |
||||
value: 2 |
||||
Label: |
||||
color: root.text_color |
||||
text: _('Require %d signatures')%m.value |
||||
Slider: |
||||
id: m |
||||
range: 1, n.value |
||||
step: 1 |
||||
value: 2 |
||||
|
||||
|
||||
<WizardChoiceDialog> |
||||
msg : '' |
||||
Widget: |
||||
size_hint: 1, 1 |
||||
Label: |
||||
color: root.text_color |
||||
size_hint: 1, None |
||||
text_size: self.width, None |
||||
height: self.texture_size[1] |
||||
text: root.msg |
||||
Widget |
||||
size_hint: 1, 1 |
||||
GridLayout: |
||||
row_default_height: '48dp' |
||||
orientation: 'vertical' |
||||
id: choices |
||||
cols: 1 |
||||
spacing: '14dp' |
||||
size_hint: 1, None |
||||
|
||||
<MButton@Button>: |
||||
size_hint: 1, None |
||||
height: '33dp' |
||||
on_release: |
||||
self.parent.update_amount(self.text) |
||||
|
||||
<WordButton@Button>: |
||||
size_hint: None, None |
||||
padding: '5dp', '5dp' |
||||
text_size: None, self.height |
||||
width: self.texture_size[0] |
||||
height: '30dp' |
||||
on_release: |
||||
self.parent.new_word(self.text) |
||||
|
||||
|
||||
<SeedButton@Button>: |
||||
height: dp(100) |
||||
border: 4, 4, 4, 4 |
||||
halign: 'justify' |
||||
valign: 'top' |
||||
font_size: '18dp' |
||||
text_size: self.width - dp(24), self.height - dp(12) |
||||
color: .1, .1, .1, 1 |
||||
background_normal: 'atlas://gui/kivy/theming/light/white_bg_round_top' |
||||
background_down: self.background_normal |
||||
size_hint_y: None |
||||
|
||||
|
||||
<SeedLabel@Label>: |
||||
font_size: '12sp' |
||||
text_size: self.width, None |
||||
size_hint: 1, None |
||||
height: self.texture_size[1] |
||||
halign: 'justify' |
||||
valign: 'middle' |
||||
border: 4, 4, 4, 4 |
||||
|
||||
|
||||
<RestoreSeedDialog> |
||||
word: '' |
||||
BigLabel: |
||||
text: "ENTER YOUR SEED PHRASE" |
||||
GridLayout |
||||
cols: 1 |
||||
padding: 0, '12dp' |
||||
orientation: 'vertical' |
||||
spacing: '12dp' |
||||
size_hint: 1, None |
||||
height: self.minimum_height |
||||
SeedButton: |
||||
id: text_input_seed |
||||
text: '' |
||||
on_text: Clock.schedule_once(root.on_text) |
||||
SeedLabel: |
||||
text: root.message |
||||
BoxLayout: |
||||
id: suggestions |
||||
height: '35dp' |
||||
size_hint: 1, None |
||||
new_word: root.on_word |
||||
BoxLayout: |
||||
id: line1 |
||||
update_amount: root.update_text |
||||
size_hint: 1, None |
||||
height: '30dp' |
||||
MButton: |
||||
text: 'Q' |
||||
MButton: |
||||
text: 'W' |
||||
MButton: |
||||
text: 'E' |
||||
MButton: |
||||
text: 'R' |
||||
MButton: |
||||
text: 'T' |
||||
MButton: |
||||
text: 'Y' |
||||
MButton: |
||||
text: 'U' |
||||
MButton: |
||||
text: 'I' |
||||
MButton: |
||||
text: 'O' |
||||
MButton: |
||||
text: 'P' |
||||
BoxLayout: |
||||
id: line2 |
||||
update_amount: root.update_text |
||||
size_hint: 1, None |
||||
height: '30dp' |
||||
Widget: |
||||
size_hint: 0.5, None |
||||
height: '33dp' |
||||
MButton: |
||||
text: 'A' |
||||
MButton: |
||||
text: 'S' |
||||
MButton: |
||||
text: 'D' |
||||
MButton: |
||||
text: 'F' |
||||
MButton: |
||||
text: 'G' |
||||
MButton: |
||||
text: 'H' |
||||
MButton: |
||||
text: 'J' |
||||
MButton: |
||||
text: 'K' |
||||
MButton: |
||||
text: 'L' |
||||
Widget: |
||||
size_hint: 0.5, None |
||||
height: '33dp' |
||||
BoxLayout: |
||||
id: line3 |
||||
update_amount: root.update_text |
||||
size_hint: 1, None |
||||
height: '30dp' |
||||
Widget: |
||||
size_hint: 1, None |
||||
MButton: |
||||
text: 'Z' |
||||
MButton: |
||||
text: 'X' |
||||
MButton: |
||||
text: 'C' |
||||
MButton: |
||||
text: 'V' |
||||
MButton: |
||||
text: 'B' |
||||
MButton: |
||||
text: 'N' |
||||
MButton: |
||||
text: 'M' |
||||
MButton: |
||||
text: ' ' |
||||
MButton: |
||||
text: '<' |
||||
|
||||
<AddXpubDialog> |
||||
title: '' |
||||
message: '' |
||||
BigLabel: |
||||
text: root.title |
||||
GridLayout |
||||
cols: 1 |
||||
padding: 0, '12dp' |
||||
orientation: 'vertical' |
||||
spacing: '12dp' |
||||
size_hint: 1, None |
||||
height: self.minimum_height |
||||
SeedButton: |
||||
id: text_input |
||||
text: '' |
||||
on_text: Clock.schedule_once(root.check_text) |
||||
SeedLabel: |
||||
text: root.message |
||||
GridLayout |
||||
rows: 1 |
||||
spacing: '12dp' |
||||
size_hint: 1, None |
||||
height: self.minimum_height |
||||
IconButton: |
||||
id: scan |
||||
height: '48sp' |
||||
on_release: root.scan_xpub() |
||||
icon: 'atlas://gui/kivy/theming/light/camera' |
||||
size_hint: 1, None |
||||
WizardButton: |
||||
text: _('Paste') |
||||
on_release: root.do_paste() |
||||
WizardButton: |
||||
text: _('Clear') |
||||
on_release: root.do_clear() |
||||
|
||||
|
||||
<ShowXpubDialog> |
||||
xpub: '' |
||||
message: _('Here is your master public key. Share it with your cosigners.') |
||||
BigLabel: |
||||
text: "MASTER PUBLIC KEY" |
||||
GridLayout |
||||
cols: 1 |
||||
padding: 0, '12dp' |
||||
orientation: 'vertical' |
||||
spacing: '12dp' |
||||
size_hint: 1, None |
||||
height: self.minimum_height |
||||
SeedButton: |
||||
id: text_input |
||||
text: root.xpub |
||||
SeedLabel: |
||||
text: root.message |
||||
GridLayout |
||||
rows: 1 |
||||
spacing: '12dp' |
||||
size_hint: 1, None |
||||
height: self.minimum_height |
||||
WizardButton: |
||||
text: _('QR code') |
||||
on_release: root.do_qr() |
||||
WizardButton: |
||||
text: _('Copy') |
||||
on_release: root.do_copy() |
||||
WizardButton: |
||||
text: _('Share') |
||||
on_release: root.do_share() |
||||
|
||||
|
||||
<ShowSeedDialog> |
||||
spacing: '12dp' |
||||
value: 'next' |
||||
BigLabel: |
||||
text: "PLEASE WRITE DOWN YOUR SEED PHRASE" |
||||
GridLayout: |
||||
id: grid |
||||
cols: 1 |
||||
pos_hint: {'center_y': .5} |
||||
size_hint_y: None |
||||
height: self.minimum_height |
||||
orientation: 'vertical' |
||||
spacing: '12dp' |
||||
SeedButton: |
||||
text: root.seed_text |
||||
SeedLabel: |
||||
text: root.message |
||||
''') |
||||
|
||||
|
||||
|
||||
class WizardDialog(EventsDialog): |
||||
''' Abstract dialog to be used as the base for all Create Account Dialogs |
||||
''' |
||||
crcontent = ObjectProperty(None) |
||||
|
||||
def __init__(self, **kwargs): |
||||
super(WizardDialog, self).__init__(**kwargs) |
||||
self.action = kwargs.get('action') |
||||
_trigger_size_dialog = Clock.create_trigger(self._size_dialog) |
||||
Window.bind(size=_trigger_size_dialog, |
||||
rotation=_trigger_size_dialog) |
||||
_trigger_size_dialog() |
||||
|
||||
def _size_dialog(self, dt): |
||||
app = App.get_running_app() |
||||
if app.ui_mode[0] == 'p': |
||||
self.size = Window.size |
||||
else: |
||||
#tablet |
||||
if app.orientation[0] == 'p': |
||||
#portrait |
||||
self.size = Window.size[0]/1.67, Window.size[1]/1.4 |
||||
else: |
||||
self.size = Window.size[0]/2.5, Window.size[1] |
||||
|
||||
def add_widget(self, widget, index=0): |
||||
if not self.crcontent: |
||||
super(WizardDialog, self).add_widget(widget) |
||||
else: |
||||
self.crcontent.add_widget(widget, index=index) |
||||
|
||||
def on_dismiss(self): |
||||
app = App.get_running_app() |
||||
if app.wallet is None and self._on_release is not None: |
||||
app.stop() |
||||
|
||||
|
||||
class WizardMultisigDialog(WizardDialog): |
||||
pass |
||||
|
||||
class WizardChoiceDialog(WizardDialog): |
||||
''' Multiple choices dialog ''' |
||||
|
||||
def __init__(self, **kwargs): |
||||
super(WizardChoiceDialog, self).__init__(**kwargs) |
||||
self.msg = kwargs.get('msg', '') |
||||
choices = kwargs.get('choices', []) |
||||
layout = self.ids.choices |
||||
layout.bind(minimum_height=layout.setter('height')) |
||||
for text, action in choices: |
||||
l = WizardButton(text=text) |
||||
l.action = action |
||||
l.height = '48dp' |
||||
l.root = self |
||||
layout.add_widget(l) |
||||
|
||||
def on_parent(self, instance, value): |
||||
if value: |
||||
app = App.get_running_app() |
||||
self._back = _back = partial(app.dispatch, 'on_back') |
||||
|
||||
|
||||
class ShowSeedDialog(WizardDialog): |
||||
|
||||
seed_text = StringProperty('') |
||||
message = StringProperty('') |
||||
|
||||
def on_parent(self, instance, value): |
||||
if value: |
||||
app = App.get_running_app() |
||||
self._back = _back = partial(self.ids.back.dispatch, 'on_release') |
||||
|
||||
|
||||
class WordButton(Button): |
||||
pass |
||||
|
||||
class WizardButton(Button): |
||||
pass |
||||
|
||||
class RestoreSeedDialog(WizardDialog): |
||||
|
||||
message = StringProperty('') |
||||
|
||||
def __init__(self, **kwargs): |
||||
super(RestoreSeedDialog, self).__init__(**kwargs) |
||||
self._test = kwargs['test'] |
||||
from electrum.mnemonic import Mnemonic |
||||
from electrum.old_mnemonic import words as old_wordlist |
||||
self.words = set(Mnemonic('en').wordlist).union(set(old_wordlist)) |
||||
self.ids.text_input_seed.text = test_seed if is_test else '' |
||||
|
||||
def get_suggestions(self, prefix): |
||||
for w in self.words: |
||||
if w.startswith(prefix): |
||||
yield w |
||||
|
||||
def on_text(self, dt): |
||||
self.ids.next.disabled = not bool(self._test(self.get_text())) |
||||
|
||||
text = self.ids.text_input_seed.text |
||||
if not text: |
||||
last_word = '' |
||||
elif text[-1] == ' ': |
||||
last_word = '' |
||||
else: |
||||
last_word = text.split(' ')[-1] |
||||
|
||||
enable_space = False |
||||
self.ids.suggestions.clear_widgets() |
||||
suggestions = [x for x in self.get_suggestions(last_word)] |
||||
if suggestions and len(suggestions) < 10: |
||||
for w in suggestions: |
||||
if w == last_word: |
||||
enable_space = True |
||||
else: |
||||
b = WordButton(text=w) |
||||
self.ids.suggestions.add_widget(b) |
||||
|
||||
i = len(last_word) |
||||
p = set() |
||||
for x in suggestions: |
||||
if len(x)>i: p.add(x[i]) |
||||
|
||||
for line in [self.ids.line1, self.ids.line2, self.ids.line3]: |
||||
for c in line.children: |
||||
if isinstance(c, Button): |
||||
if c.text in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ': |
||||
c.disabled = (c.text.lower() not in p) and last_word |
||||
elif c.text == ' ': |
||||
c.disabled = not enable_space |
||||
|
||||
def on_word(self, w): |
||||
text = self.get_text() |
||||
words = text.split(' ') |
||||
words[-1] = w |
||||
text = ' '.join(words) |
||||
self.ids.text_input_seed.text = text + ' ' |
||||
self.ids.suggestions.clear_widgets() |
||||
|
||||
def get_text(self): |
||||
ti = self.ids.text_input_seed |
||||
text = unicode(ti.text).strip() |
||||
text = ' '.join(text.split()) |
||||
return text |
||||
|
||||
def update_text(self, c): |
||||
c = c.lower() |
||||
text = self.ids.text_input_seed.text |
||||
if c == '<': |
||||
text = text[:-1] |
||||
else: |
||||
text += c |
||||
self.ids.text_input_seed.text = text |
||||
|
||||
def on_parent(self, instance, value): |
||||
if value: |
||||
tis = self.ids.text_input_seed |
||||
tis.focus = True |
||||
#tis._keyboard.bind(on_key_down=self.on_key_down) |
||||
self._back = _back = partial(self.ids.back.dispatch, |
||||
'on_release') |
||||
app = App.get_running_app() |
||||
|
||||
def on_key_down(self, keyboard, keycode, key, modifiers): |
||||
if keycode[0] in (13, 271): |
||||
self.on_enter() |
||||
return True |
||||
|
||||
def on_enter(self): |
||||
#self._remove_keyboard() |
||||
# press next |
||||
next = self.ids.next |
||||
if not next.disabled: |
||||
next.dispatch('on_release') |
||||
|
||||
def _remove_keyboard(self): |
||||
tis = self.ids.text_input_seed |
||||
if tis._keyboard: |
||||
tis._keyboard.unbind(on_key_down=self.on_key_down) |
||||
tis.focus = False |
||||
|
||||
|
||||
class ShowXpubDialog(WizardDialog): |
||||
|
||||
def __init__(self, **kwargs): |
||||
WizardDialog.__init__(self, **kwargs) |
||||
self.app = App.get_running_app() |
||||
self.xpub = kwargs['xpub'] |
||||
self.ids.next.disabled = False |
||||
|
||||
def do_copy(self): |
||||
self.app._clipboard.copy(self.xpub) |
||||
|
||||
def do_share(self): |
||||
self.app.do_share(self.xpub, _("Master Public Key")) |
||||
|
||||
def do_qr(self): |
||||
from qr_dialog import QRDialog |
||||
popup = QRDialog(_("Master Public Key"), self.xpub, True) |
||||
popup.open() |
||||
|
||||
|
||||
class AddXpubDialog(WizardDialog): |
||||
|
||||
def __init__(self, **kwargs): |
||||
WizardDialog.__init__(self, **kwargs) |
||||
self.app = App.get_running_app() |
||||
self._test = kwargs['test'] |
||||
self.title = kwargs['title'] |
||||
self.message = kwargs['message'] |
||||
|
||||
def check_text(self, dt): |
||||
self.ids.next.disabled = not bool(self._test(self.get_text())) |
||||
|
||||
def get_text(self): |
||||
ti = self.ids.text_input |
||||
return unicode(ti.text).strip() |
||||
|
||||
def scan_xpub(self): |
||||
def on_complete(text): |
||||
self.ids.text_input.text = text |
||||
self.app.scan_qr(on_complete) |
||||
|
||||
def do_paste(self): |
||||
self.ids.text_input.text = test_xpub if is_test else unicode(self.app._clipboard.paste()) |
||||
|
||||
def do_clear(self): |
||||
self.ids.text_input.text = '' |
||||
@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python |
||||
# |
||||
# Electrum - lightweight Bitcoin client |
||||
# Copyright (C) 2016 Thomas Voegtlin |
||||
# |
||||
# Permission is hereby granted, free of charge, to any person |
||||
# obtaining a copy of this software and associated documentation files |
||||
# (the "Software"), to deal in the Software without restriction, |
||||
# including without limitation the rights to use, copy, modify, merge, |
||||
# publish, distribute, sublicense, and/or sell copies of the Software, |
||||
# and to permit persons to whom the Software is furnished to do so, |
||||
# subject to the following conditions: |
||||
# |
||||
# The above copyright notice and this permission notice shall be |
||||
# included in all copies or substantial portions of the Software. |
||||
# |
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
||||
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
||||
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
||||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
||||
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
||||
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
# SOFTWARE. |
||||
|
||||
import os |
||||
from electrum.wallet import Wallet, Multisig_Wallet |
||||
from electrum_gui.kivy.i18n import _ |
||||
|
||||
|
||||
class BaseWizard(object): |
||||
|
||||
def __init__(self, config, network, storage): |
||||
super(BaseWizard, self).__init__() |
||||
self.config = config |
||||
self.network = network |
||||
self.storage = storage |
||||
self.wallet = None |
||||
|
||||
def run(self, action, *args): |
||||
'''Entry point of our Installation wizard''' |
||||
if not action: |
||||
return |
||||
if hasattr(self, action): |
||||
f = getattr(self, action) |
||||
apply(f, *args) |
||||
else: |
||||
raise BaseException("unknown action", action) |
||||
|
||||
def new(self): |
||||
name = os.path.basename(self.storage.path) |
||||
msg = "\n".join([ |
||||
_("Welcome to the Electrum installation wizard."), |
||||
_("The wallet '%s' does not exist.") % name, |
||||
_("What kind of wallet do you want to create?") |
||||
]) |
||||
choices = [ |
||||
(_('Standard wallet'), 'create_standard'), |
||||
(_('Multi-signature wallet'), 'create_multisig'), |
||||
] |
||||
self.choice_dialog(msg=msg, choices=choices, run_prev=self.cancel, run_next=self.run) |
||||
|
||||
def choose_seed(self): |
||||
msg = ' '.join([ |
||||
_("Do you want to create a new seed, or to restore a wallet using an existing seed?") |
||||
]) |
||||
choices = [ |
||||
(_('Create a new seed'), 'create_seed'), |
||||
(_('I already have a seed'), 'restore_seed'), |
||||
(_('Watching-only wallet'), 'restore_xpub') |
||||
] |
||||
self.choice_dialog(msg=msg, choices=choices, run_prev=self.new, run_next=self.run) |
||||
|
||||
def create_multisig(self): |
||||
def f(m, n): |
||||
self.wallet_type = "%dof%d"%(m, n) |
||||
self.run('choose_seed') |
||||
name = os.path.basename(self.storage.path) |
||||
self.multisig_dialog(run_prev=self.new, run_next=f) |
||||
|
||||
def restore_seed(self): |
||||
msg = _('Please type your seed phrase using the virtual keyboard.') |
||||
self.restore_seed_dialog(run_prev=self.new, run_next=self.enter_pin, test=Wallet.is_seed, message=msg) |
||||
|
||||
def restore_xpub(self): |
||||
title = "MASTER PUBLIC KEY" |
||||
message = _('To create a watching-only wallet, paste your master public key, or scan it using the camera button.') |
||||
self.add_xpub_dialog(run_prev=self.new, run_next=lambda xpub: self.create_wallet(xpub, None), title=title, message=message, test=Wallet.is_mpk) |
||||
|
||||
def create_standard(self): |
||||
self.wallet_type = 'standard' |
||||
self.run('choose_seed') |
||||
|
||||
def create_wallet(self, text, password): |
||||
if self.wallet_type == 'standard': |
||||
self.wallet = Wallet.from_text(text, password, self.storage) |
||||
self.run('create_addresses') |
||||
else: |
||||
self.storage.put('wallet_type', self.wallet_type) |
||||
self.wallet = Multisig_Wallet(self.storage) |
||||
self.wallet.add_seed(text, password) |
||||
self.wallet.create_master_keys(password) |
||||
action = self.wallet.get_action() |
||||
self.run(action) |
||||
|
||||
def add_cosigners(self): |
||||
xpub = self.wallet.master_public_keys.get('x1/') |
||||
self.show_xpub_dialog(run_prev=self.create_multisig, run_next=self.add_cosigner, xpub=xpub, test=Wallet.is_xpub) |
||||
|
||||
def add_cosigner(self): |
||||
def on_xpub(xpub): |
||||
self.wallet.add_cosigner(xpub) |
||||
i = self.wallet.get_missing_cosigner() |
||||
action = 'add_cosigner' if i else 'create_main_account' |
||||
self.run(action) |
||||
title = "ADD COSIGNER" |
||||
message = _('Please paste your cosigners master public key, or scan it using the camera button.') |
||||
self.add_xpub_dialog(run_prev=self.add_cosigners, run_next=on_xpub, title=title, message=message, test=Wallet.is_xpub) |
||||
|
||||
def create_main_account(self): |
||||
self.wallet.create_main_account() |
||||
self.run('create_addresses') |
||||
|
||||
def create_addresses(self): |
||||
def task(): |
||||
self.wallet.create_main_account() |
||||
self.wallet.synchronize() |
||||
msg= _("Electrum is generating your addresses, please wait.") |
||||
self.waiting_dialog(task, msg, on_complete=self.terminate) |
||||
|
||||
def create_seed(self): |
||||
from electrum.wallet import BIP32_Wallet |
||||
seed = BIP32_Wallet.make_seed() |
||||
msg = _("If you forget your PIN or lose your device, your seed phrase will be the " |
||||
"only way to recover your funds.") |
||||
self.show_seed_dialog(run_prev=self.new, run_next=self.confirm_seed, message=msg, seed_text=seed) |
||||
|
||||
def confirm_seed(self, seed): |
||||
assert Wallet.is_seed(seed) |
||||
msg = _('Please retype your seed phrase, to confirm that you properly saved it') |
||||
self.restore_seed_dialog(run_prev=self.create_seed, run_next=self.enter_pin, test=lambda x: x==seed, message=msg) |
||||
|
||||
def enter_pin(self, seed): |
||||
def callback(pin): |
||||
action = 'confirm_pin' if pin else 'create_wallet' |
||||
self.run(action, (seed, pin)) |
||||
self.password_dialog('Choose a PIN code', callback) |
||||
|
||||
def confirm_pin(self, seed, pin): |
||||
def callback(conf): |
||||
if conf == pin: |
||||
self.run('create_wallet', (seed, pin)) |
||||
else: |
||||
self.show_error(_('PIN mismatch')) |
||||
self.run('enter_pin', (seed,)) |
||||
self.password_dialog('Confirm your PIN code', callback) |
||||
|
||||
def terminate(self): |
||||
self.wallet.start_threads(self.network) |
||||
self.dispatch('on_wizard_complete', self.wallet) |
||||
|
||||
def cancel(self): |
||||
self.dispatch('on_wizard_complete', None) |
||||
return True |
||||
|
||||
|
||||
|
||||
Loading…
Reference in new issue