Example #1
0
 def __init__(self, config, network, path):
     super(BaseWizard, self).__init__()
     self.config = config
     self.network = network
     self.storage = WalletStorage(path)
     self.wallet = None
     self.stack = []
     self.plugin = None
Example #2
0
    def update(self, pk):
        invested = self.data.get('settings_investment')
        currency = self.data.get('settings_currency')

        WalletStorage.update(pk, fiat_currency=currency, invested=invested)
        wallet = WalletStorage.get(pk)
        wallet.update()

        return wallet.get_all()
Example #3
0
 def __init__(self, config, path):
     super(BaseWizard, self).__init__()
     self.config = config
     self.storage = WalletStorage(path)
     self.wallet = None
     self.stack = []
     self.plugin = None
     self.keystores = []
     self.is_kivy = config.get('gui') == 'kivy'
Example #4
0
 def load_wallet(self, path):
     # wizard will be launched if we return
     if path in self.wallets:
         wallet = self.wallets[path]
         return wallet
     storage = WalletStorage(path)
     if not storage.file_exists:
         return
     if storage.requires_split() or storage.requires_upgrade() or storage.get_action():
         return
     wallet = Wallet(storage)
     wallet.start_threads(self.network)
     self.wallets[path] = wallet
     return wallet
Example #5
0
 def load_wallet(self, path):
     # wizard will be launched if we return
     if path in self.wallets:
         wallet = self.wallets[path]
         return wallet
     storage = WalletStorage(path)
     if not storage.file_exists:
         return
     if storage.requires_split() or storage.requires_upgrade() or storage.get_action():
         return
     wallet = Wallet(storage)
     wallet.start_threads(self.network)
     self.wallets[path] = wallet
     return wallet
Example #6
0
    def load_wallet(self, path, get_wizard=None):
        if path in self.wallets:
            wallet = self.wallets[path]
        else:
            storage = WalletStorage(path)
            if get_wizard:
                if storage.file_exists:
                    wallet = Wallet(storage)
                    action = wallet.get_action()
                else:
                    action = 'new'
                if action:
                    wizard = get_wizard()
                    wallet = wizard.run(self.network, storage)
                else:
                    wallet.start_threads(self.network)
            else:
                wallet = Wallet(storage)
                # automatically generate wallet for lbrynet
                if not storage.file_exists:
                    seed = wallet.make_seed()
                    wallet.add_seed(seed, None)
                    wallet.create_master_keys(None)
                    wallet.create_main_account()
                    wallet.synchronize()

                wallet.start_threads(self.network)
            if wallet:
                self.wallets[path] = wallet
        return wallet
Example #7
0
 def __init__(self, config, network, path):
     super(BaseWizard, self).__init__()
     self.config = config
     self.network = network
     self.storage = WalletStorage(path)
     self.wallet = None
     self.stack = []
     self.plugin = None
Example #8
0
 def __init__(self, config, path):
     super(BaseWizard, self).__init__()
     self.config = config
     self.storage = WalletStorage(path)
     self.wallet = None
     self.stack = []
     self.plugin = None
     self.keystores = []
     self.is_kivy = config.get('gui') == 'kivy'
Example #9
0
 def load_wallet(self, config):
     path = config.get_wallet_path()
     if path in self.wallets:
         wallet = self.wallets[path]
     else:
         storage = WalletStorage(path)
         wallet = Wallet(storage)
         wallet.start_threads(self.network)
         self.wallets[path] = wallet
     return wallet
Example #10
0
    def is_authenticated(self):
        if self.request.method == 'POST':
            return True

        pk = self.request.view_args.get('pk', None)

        if not pk:
            return False

        a = WalletStorage.exists(pk)
        return a
Example #11
0
    def create(self):
        if not self.data:
            return "", 555

        invested = self.data.get('settings_investment')
        currency = self.data.get('settings_currency')
        coins_address = self.data.get('settings_coins_address')

        wallet = WalletStorage.create([coins_address], currency, invested)
        wallet.update()

        return wallet.get_all()
Example #12
0
 def load_wallet(self, path):
     if path in self.wallets:
         wallet = self.wallets[path]
         return wallet
     storage = WalletStorage(path)
     if not storage.file_exists:
         return
     wallet = Wallet(storage)
     action = wallet.get_action()
     if action:
         return
     wallet.start_threads(self.network)
     self.wallets[path] = wallet
     return wallet
Example #13
0
 def load_wallet(self, path):
     # wizard will be launched if we return
     if path in self.wallets:
         wallet = self.wallets[path]
         return wallet
     storage = WalletStorage(path)
     if not storage.file_exists:
         return
     if storage.requires_upgrade() and 'ANDROID_DATA' in os.environ:
         self.print_error('upgrading wallet format')
         storage.upgrade()
     if storage.requires_split() or storage.requires_upgrade(
     ) or storage.get_action():
         return
     wallet = Wallet(storage)
     wallet.start_threads(self.network)
     self.wallets[path] = wallet
     return wallet
Example #14
0
 def load_wallet(self, path, get_wizard=None):
     if path in self.wallets:
         wallet = self.wallets[path]
     else:
         storage = WalletStorage(path)
         if storage.file_exists:
             wallet = Wallet(storage)
             action = wallet.get_action()
         else:
             action = 'new'
         if action:
             if get_wizard is None:
                 return None
             wizard = get_wizard()
             wallet = wizard.run(self.network, storage)
         else:
             wallet.start_threads(self.network)
         if wallet:
             self.wallets[path] = wallet
     return wallet
Example #15
0
class BaseWizard(object):
    def __init__(self, config, network, path):
        super(BaseWizard, self).__init__()
        self.config = config
        self.network = network
        self.storage = WalletStorage(path)
        self.wallet = None
        self.stack = []
        self.plugin = None
        self.keystores = []
        self.is_kivy = config.get('gui') == 'kivy'

    def run(self, *args):
        action = args[0]
        args = args[1:]
        self.stack.append((action, args))
        if not action:
            return
        if type(action) is tuple:
            self.plugin, action = action
        if self.plugin and hasattr(self.plugin, action):
            f = getattr(self.plugin, action)
            apply(f, (self, ) + args)
        elif hasattr(self, action):
            f = getattr(self, action)
            apply(f, args)
        else:
            raise BaseException("unknown action", action)

    def can_go_back(self):
        return len(self.stack) > 1

    def go_back(self):
        if not self.can_go_back():
            return
        self.stack.pop()
        action, args = self.stack.pop()
        self.run(action, *args)

    def new(self):
        name = os.path.basename(self.storage.path)
        title = _("Welcome to the Electrum installation wizard.")
        message = '\n'.join([
            _("The wallet '%s' does not exist.") % name,
            _("What kind of wallet do you want to create?")
        ])
        wallet_kinds = [
            ('standard', _("Standard wallet")),
            ('2fa', _("Wallet with two-factor authentication")),
            ('multisig', _("Multi-signature wallet")),
            ('imported', _("Watch Bitcoin addresses")),
        ]
        choices = [pair for pair in wallet_kinds if pair[0] in wallet_types]
        self.choice_dialog(title=title,
                           message=message,
                           choices=choices,
                           run_next=self.on_wallet_type)

    def load_2fa(self):
        self.storage.put('wallet_type', '2fa')
        self.storage.put('use_trustedcoin', True)
        self.plugin = self.plugins.load_plugin('trustedcoin')

    def on_wallet_type(self, choice):
        self.wallet_type = choice
        if choice == 'standard':
            action = 'choose_keystore'
        elif choice == 'multisig':
            action = 'choose_multisig'
        elif choice == '2fa':
            self.load_2fa()
            action = self.storage.get_action()
        elif choice == 'imported':
            action = 'import_addresses'
        self.run(action)

    def choose_multisig(self):
        def on_multisig(m, n):
            self.multisig_type = "%dof%d" % (m, n)
            self.storage.put('wallet_type', self.multisig_type)
            self.n = n
            self.run('choose_keystore')

        self.multisig_dialog(run_next=on_multisig)

    def choose_keystore(self):
        assert self.wallet_type in ['standard', 'multisig']
        i = len(self.keystores)
        title = _('Add cosigner') + ' (%d of %d)' % (
            i + 1, self.n) if self.wallet_type == 'multisig' else _('Keystore')
        if self.wallet_type == 'standard' or i == 0:
            message = _(
                'Do you want to create a new seed, or to restore a wallet using an existing seed?'
            )
            choices = [
                ('create_seed', _('Create a new seed')),
                ('restore_from_seed', _('I already have a seed')),
                ('restore_from_key', _('Use public or private keys')),
            ]
            if not self.is_kivy:
                choices.append(
                    ('choose_hw_device', _('Use a hardware device')))
        else:
            message = _('Add a cosigner to your multi-sig wallet')
            choices = [
                ('restore_from_key', _('Enter cosigner key')),
                ('restore_from_seed', _('Enter cosigner seed')),
            ]
            if not self.is_kivy:
                choices.append(
                    ('choose_hw_device', _('Cosign with hardware device')))

        self.choice_dialog(title=title,
                           message=message,
                           choices=choices,
                           run_next=self.run)

    def import_addresses(self):
        v = keystore.is_address_list
        title = _("Import Bitcoin Addresses")
        message = _(
            "Enter a list of Bitcoin addresses. This will create a watching-only wallet."
        )
        self.add_xpub_dialog(title=title,
                             message=message,
                             run_next=self.on_import_addresses,
                             is_valid=v)

    def on_import_addresses(self, text):
        assert keystore.is_address_list(text)
        self.wallet = Imported_Wallet(self.storage)
        for x in text.split():
            self.wallet.import_address(x)
        self.terminate()

    def restore_from_key(self):
        if self.wallet_type == 'standard':
            v = keystore.is_any_key
            title = _("Create keystore from keys")
            message = ' '.join([
                _("To create a watching-only wallet, please enter your master public key (xpub)."
                  ),
                _("To create a spending wallet, please enter a master private key (xprv), or a list of Bitcoin private keys."
                  )
            ])
            self.add_xpub_dialog(title=title,
                                 message=message,
                                 run_next=self.on_restore_from_key,
                                 is_valid=v)
        else:
            v = keystore.is_bip32_key
            i = len(self.keystores) + 1
            self.add_cosigner_dialog(index=i,
                                     run_next=self.on_restore_from_key,
                                     is_valid=v)

    def on_restore_from_key(self, text):
        k = keystore.from_keys(text)
        self.on_keystore(k)

    def choose_hw_device(self):
        title = _('Hardware Keystore')
        # check available plugins
        support = self.plugins.get_hardware_support()
        if not support:
            msg = '\n'.join([
                _('No hardware wallet support found on your system.'),
                _('Please install the relevant libraries (eg python-trezor for Trezor).'
                  ),
            ])
            self.confirm_dialog(title=title,
                                message=msg,
                                run_next=lambda x: self.choose_hw_device())
            return
        # scan devices
        devices = []
        devmgr = self.plugins.device_manager
        for name, description, plugin in support:
            try:
                # FIXME: side-effect: unpaired_device_info sets client.handler
                u = devmgr.unpaired_device_infos(None, plugin)
            except:
                devmgr.print_error("error", name)
                continue
            devices += map(lambda x: (name, x), u)
        if not devices:
            msg = '\n'.join([
                _('No hardware device detected.'),
                _('To trigger a rescan, press \'next\'.'),
            ])
            self.confirm_dialog(title=title,
                                message=msg,
                                run_next=lambda x: self.choose_hw_device())
            return
        # select device
        self.devices = devices
        choices = []
        for name, info in devices:
            state = _("initialized") if info.initialized else _("wiped")
            label = info.label or _("An unnamed %s") % name
            descr = "%s [%s, %s]" % (label, name, state)
            choices.append(((name, info), descr))
        msg = _('Select a device') + ':'
        self.choice_dialog(title=title,
                           message=msg,
                           choices=choices,
                           run_next=self.on_device)

    def on_device(self, name, device_info):
        self.plugin = self.plugins.get_plugin(name)
        self.plugin.setup_device(device_info, self)
        if self.wallet_type == 'multisig':
            # There is no general standard for HD multisig.
            # This is partially compatible with BIP45; assumes index=0
            self.on_hw_derivation(name, device_info, "m/45'/0")
        else:
            from keystore import bip44_derivation
            f = lambda x: self.run('on_hw_derivation', name, device_info,
                                   bip44_derivation(int(x)))
            self.account_id_dialog(f)

    def account_id_dialog(self, f):
        message = '\n'.join([
            _('Enter your BIP44 account number here.'),
            _('If you are not sure what this is, leave this field to zero.')
        ])

        def is_int(x):
            try:
                int(x)
                return True
            except:
                return False

        self.line_dialog(run_next=f,
                         title=_('Account Number'),
                         message=message,
                         default='0',
                         test=is_int)

    def on_hw_derivation(self, name, device_info, derivation):
        from keystore import hardware_keystore
        xpub = self.plugin.get_xpub(device_info.device.id_, derivation, self)
        if xpub is None:
            self.show_error('Cannot read xpub from device')
            return
        d = {
            'type': 'hardware',
            'hw_type': name,
            'derivation': derivation,
            'xpub': xpub,
            'label': device_info.label,
        }
        k = hardware_keystore(d)
        self.on_keystore(k)

    def passphrase_dialog(self, run_next):
        title = _('Passphrase')
        message = '\n'.join([
            _('You may extend your seed with a passphrase.'),
            _('The passphrase must be saved together with your seed.'),
        ])
        warning = '\n'.join([
            _('Note that this is NOT your encryption password.'),
            _('If you do not know what this is, leave this field empty.'),
        ])
        self.line_dialog(title=_('Passphrase'),
                         message=message,
                         warning=warning,
                         default='',
                         test=lambda x: True,
                         run_next=run_next)

    def restore_from_seed(self):
        self.opt_bip39 = True
        self.opt_ext = True
        test = bitcoin.is_seed if self.wallet_type == 'standard' else bitcoin.is_new_seed
        self.restore_seed_dialog(run_next=self.on_restore_seed, test=test)

    def on_restore_seed(self, seed, is_bip39, is_ext):
        if is_bip39:
            f = lambda passphrase: self.on_restore_bip39(seed, passphrase)
            self.passphrase_dialog(run_next=f) if is_ext else f('')
        else:
            seed_type = bitcoin.seed_type(seed)
            if seed_type == 'standard':
                f = lambda passphrase: self.run('create_keystore', seed,
                                                passphrase)
                self.passphrase_dialog(run_next=f) if is_ext else f('')
            elif seed_type == 'old':
                self.run('create_keystore', seed, passphrase)
            elif seed_type == '2fa':
                if self.is_kivy:
                    self.show_error(
                        '2FA seeds are not supported in this version')
                    self.run('restore_from_seed')
                else:
                    self.load_2fa()
                    self.run('on_restore_seed', seed, is_ext)
            else:
                raise

    def on_restore_bip39(self, seed, passphrase):
        f = lambda x: self.run('on_bip44', seed, passphrase, int(x))
        self.account_id_dialog(f)

    def create_keystore(self, seed, passphrase):
        k = keystore.from_seed(seed, passphrase)
        self.on_keystore(k)

    def on_bip44(self, seed, passphrase, account_id):
        k = keystore.BIP32_KeyStore({})
        bip32_seed = keystore.bip39_to_seed(seed, passphrase)
        derivation = "m/44'/0'/%d'" % account_id
        k.add_xprv_from_seed(bip32_seed, derivation)
        self.on_keystore(k)

    def on_keystore(self, k):
        if self.wallet_type == 'standard':
            self.keystores.append(k)
            self.run('create_wallet')
        elif self.wallet_type == 'multisig':
            if k.xpub in map(lambda x: x.xpub, self.keystores):
                self.show_error(_('Error: duplicate master public key'))
                self.run('choose_keystore')
                return
            self.keystores.append(k)
            if len(self.keystores) == 1:
                xpub = k.get_master_public_key()
                self.stack = []
                self.run('show_xpub_and_add_cosigners', xpub)
            elif len(self.keystores) < self.n:
                self.run('choose_keystore')
            else:
                self.run('create_wallet')

    def create_wallet(self):
        if any(k.may_have_password() for k in self.keystores):
            self.request_password(run_next=self.on_password)
        else:
            self.on_password(None)

    def on_password(self, password):
        self.storage.put('use_encryption', bool(password))
        for k in self.keystores:
            if k.may_have_password():
                k.update_password(None, password)
        if self.wallet_type == 'standard':
            self.storage.put('keystore', k.dump())
            self.wallet = Standard_Wallet(self.storage)
            self.run('create_addresses')
        elif self.wallet_type == 'multisig':
            for i, k in enumerate(self.keystores):
                self.storage.put('x%d/' % (i + 1), k.dump())
            self.storage.write()
            self.wallet = Multisig_Wallet(self.storage)
            self.run('create_addresses')

    def show_xpub_and_add_cosigners(self, xpub):
        self.show_xpub_dialog(xpub=xpub,
                              run_next=lambda x: self.run('choose_keystore'))

    def add_cosigners(self, password, i):
        self.add_cosigner_dialog(
            run_next=lambda x: self.on_cosigner(x, password, i),
            index=i,
            is_valid=keystore.is_xpub)

    def on_cosigner(self, text, password, i):
        k = keystore.from_keys(text, password)
        self.on_keystore(k)

    def create_seed(self):
        from electrum.mnemonic import Mnemonic
        seed = Mnemonic('en').make_seed()
        self.opt_bip39 = False
        f = lambda x: self.request_passphrase(seed, x)
        self.show_seed_dialog(run_next=f, seed_text=seed)

    def request_passphrase(self, seed, opt_passphrase):
        if opt_passphrase:
            f = lambda x: self.confirm_seed(seed, x)
            self.passphrase_dialog(run_next=f)
        else:
            self.run('confirm_seed', seed, '')

    def confirm_seed(self, seed, passphrase):
        f = lambda x: self.confirm_passphrase(seed, passphrase)
        self.confirm_seed_dialog(run_next=f, test=lambda x: x == seed)

    def confirm_passphrase(self, seed, passphrase):
        f = lambda x: self.run('create_keystore', seed, x)
        if passphrase:
            title = _('Confirm Passphrase')
            message = '\n'.join([
                _('Your passphrase must be saved together with your seed.'),
                _('Please type it here.'),
            ])
            self.line_dialog(run_next=f,
                             title=title,
                             message=message,
                             default='',
                             test=lambda x: x == passphrase)
        else:
            f('')

    def create_addresses(self):
        def task():
            self.wallet.synchronize()
            self.wallet.storage.write()
            self.terminate()

        msg = _("Electrum is generating your addresses, please wait.")
        self.waiting_dialog(task, msg)
Example #16
0
    def detail(self, pk):
        wallet = WalletStorage.get(pk)
        wallet.update()

        return wallet.get_all()
Example #17
0
class BaseWizard(object):

    def __init__(self, config, path):
        super(BaseWizard, self).__init__()
        self.config = config
        self.storage = WalletStorage(path)
        self.wallet = None
        self.stack = []
        self.plugin = None
        self.keystores = []
        self.is_kivy = config.get('gui') == 'kivy'

    def run(self, *args):
        action = args[0]
        args = args[1:]
        self.stack.append((action, args))
        if not action:
            return
        if type(action) is tuple:
            self.plugin, action = action
        if self.plugin and hasattr(self.plugin, action):
            f = getattr(self.plugin, action)
            apply(f, (self,) + args)
        elif hasattr(self, action):
            f = getattr(self, action)
            apply(f, args)
        else:
            raise BaseException("unknown action", action)

    def can_go_back(self):
        return len(self.stack)>1

    def go_back(self):
        if not self.can_go_back():
            return
        self.stack.pop()
        action, args = self.stack.pop()
        self.run(action, *args)

    def new(self):
        name = os.path.basename(self.storage.path)
        title = _("Welcome to the Electrum installation wizard.")
        message = '\n'.join([
            _("The wallet '%s' does not exist.") % name,
            _("What kind of wallet do you want to create?")
        ])
        wallet_kinds = [
            ('standard',  _("Standard wallet")),
            ('2fa', _("Wallet with two-factor authentication")),
            ('multisig',  _("Multi-signature wallet")),
            ('imported',  _("Watch Bitcoin addresses")),
        ]
        choices = [pair for pair in wallet_kinds if pair[0] in wallet_types]
        self.choice_dialog(title=title, message=message, choices=choices, run_next=self.on_wallet_type)

    def load_2fa(self):
        self.storage.put('wallet_type', '2fa')
        self.storage.put('use_trustedcoin', True)
        self.plugin = self.plugins.load_plugin('trustedcoin')

    def on_wallet_type(self, choice):
        self.wallet_type = choice
        if choice == 'standard':
            action = 'choose_keystore'
        elif choice == 'multisig':
            action = 'choose_multisig'
        elif choice == '2fa':
            self.load_2fa()
            action = self.storage.get_action()
        elif choice == 'imported':
            action = 'import_addresses'
        self.run(action)

    def choose_multisig(self):
        def on_multisig(m, n):
            self.multisig_type = "%dof%d"%(m, n)
            self.storage.put('wallet_type', self.multisig_type)
            self.n = n
            self.run('choose_keystore')
        self.multisig_dialog(run_next=on_multisig)

    def choose_keystore(self):
        assert self.wallet_type in ['standard', 'multisig']
        i = len(self.keystores)
        title = _('Add cosigner') + ' (%d of %d)'%(i+1, self.n) if self.wallet_type=='multisig' else _('Keystore')
        if self.wallet_type =='standard' or i==0:
            message = _('Do you want to create a new seed, or to restore a wallet using an existing seed?')
            choices = [
                ('create_seed', _('Create a new seed')),
                ('restore_from_seed', _('I already have a seed')),
                ('restore_from_key', _('Use public or private keys')),
            ]
            if not self.is_kivy:
                choices.append(('choose_hw_device',  _('Use a hardware device')))
        else:
            message = _('Add a cosigner to your multi-sig wallet')
            choices = [
                ('restore_from_key', _('Enter cosigner key')),
                ('restore_from_seed', _('Enter cosigner seed')),
            ]
            if not self.is_kivy:
                choices.append(('choose_hw_device',  _('Cosign with hardware device')))

        self.choice_dialog(title=title, message=message, choices=choices, run_next=self.run)

    def import_addresses(self):
        v = keystore.is_address_list
        title = _("Import Bitcoin Addresses")
        message = _("Enter a list of Bitcoin addresses. This will create a watching-only wallet.")
        self.add_xpub_dialog(title=title, message=message, run_next=self.on_import_addresses, is_valid=v)

    def on_import_addresses(self, text):
        assert keystore.is_address_list(text)
        self.wallet = Imported_Wallet(self.storage)
        for x in text.split():
            self.wallet.import_address(x)
        self.terminate()

    def restore_from_key(self):
        if self.wallet_type == 'standard':
            v = keystore.is_any_key
            title = _("Create keystore from keys")
            message = ' '.join([
                _("To create a watching-only wallet, please enter your master public key (xpub)."),
                _("To create a spending wallet, please enter a master private key (xprv), or a list of Bitcoin private keys.")
            ])
            self.add_xpub_dialog(title=title, message=message, run_next=self.on_restore_from_key, is_valid=v)
        else:
            v = keystore.is_bip32_key
            i = len(self.keystores) + 1
            self.add_cosigner_dialog(index=i, run_next=self.on_restore_from_key, is_valid=v)

    def on_restore_from_key(self, text):
        k = keystore.from_keys(text)
        self.on_keystore(k)

    def choose_hw_device(self):
        title = _('Hardware Keystore')
        # check available plugins
        support = self.plugins.get_hardware_support()
        if not support:
            msg = '\n'.join([
                _('No hardware wallet support found on your system.'),
                _('Please install the relevant libraries (eg python-trezor for Trezor).'),
            ])
            self.confirm_dialog(title=title, message=msg, run_next= lambda x: self.choose_hw_device())
            return
        # scan devices
        devices = []
        devmgr = self.plugins.device_manager
        for name, description, plugin in support:
            try:
                # FIXME: side-effect: unpaired_device_info sets client.handler
                u = devmgr.unpaired_device_infos(None, plugin)
            except:
                devmgr.print_error("error", name)
                continue
            devices += map(lambda x: (name, x), u)
        if not devices:
            msg = '\n'.join([
                _('No hardware device detected.'),
                _('To trigger a rescan, press \'next\'.'),
            ])
            self.confirm_dialog(title=title, message=msg, run_next= lambda x: self.choose_hw_device())
            return
        # select device
        self.devices = devices
        choices = []
        for name, info in devices:
            state = _("initialized") if info.initialized else _("wiped")
            label = info.label or _("An unnamed %s")%name
            descr = "%s [%s, %s]" % (label, name, state)
            choices.append(((name, info), descr))
        msg = _('Select a device') + ':'
        self.choice_dialog(title=title, message=msg, choices=choices, run_next=self.on_device)

    def on_device(self, name, device_info):
        self.plugin = self.plugins.get_plugin(name)
        try:
            self.plugin.setup_device(device_info, self)
        except BaseException as e:
            self.show_error(str(e))
            self.choose_hw_device()
            return
        if self.wallet_type=='multisig':
            # There is no general standard for HD multisig.
            # This is partially compatible with BIP45; assumes index=0
            self.on_hw_derivation(name, device_info, "m/45'/0")
        else:
            from keystore import bip44_derivation
            f = lambda x: self.run('on_hw_derivation', name, device_info, bip44_derivation(int(x)))
            self.account_id_dialog(f)

    def account_id_dialog(self, f):
        message = '\n'.join([
            _('Enter your BIP44 account number here.'),
            _('If you are not sure what this is, leave this field to zero.')
        ])
        def is_int(x):
            try:
                int(x)
                return True
            except:
                return False
        self.line_dialog(run_next=f, title=_('Account Number'), message=message, default='0', test=is_int)

    def on_hw_derivation(self, name, device_info, derivation):
        from keystore import hardware_keystore
        xpub = self.plugin.get_xpub(device_info.device.id_, derivation, self)
        if xpub is None:
            self.show_error('Cannot read xpub from device')
            return
        d = {
            'type': 'hardware',
            'hw_type': name,
            'derivation': derivation,
            'xpub': xpub,
            'label': device_info.label,
        }
        k = hardware_keystore(d)
        self.on_keystore(k)

    def passphrase_dialog(self, run_next):
        title = _('Seed extension')
        message = '\n'.join([
            _('You may extend your seed with custom words.'),
            _('Your seed extension must be saved together with your seed.'),
        ])
        warning = '\n'.join([
            _('Note that this is NOT your encryption password.'),
            _('If you do not know what this is, leave this field empty.'),
        ])
        self.line_dialog(title=title, message=message, warning=warning, default='', test=lambda x:True, run_next=run_next)

    def restore_from_seed(self):
        self.opt_bip39 = True
        self.opt_ext = True
        test = bitcoin.is_seed if self.wallet_type == 'standard' else bitcoin.is_new_seed
        self.restore_seed_dialog(run_next=self.on_restore_seed, test=test)

    def on_restore_seed(self, seed, is_bip39, is_ext):
        if is_bip39:
            f = lambda passphrase: self.on_restore_bip39(seed, passphrase)
            self.passphrase_dialog(run_next=f) if is_ext else f('')
        else:
            seed_type = bitcoin.seed_type(seed)
            if seed_type == 'standard':
                f = lambda passphrase: self.run('create_keystore', seed, passphrase)
                self.passphrase_dialog(run_next=f) if is_ext else f('')
            elif seed_type == 'old':
                self.run('create_keystore', seed, '')
            elif seed_type == '2fa':
                if self.is_kivy:
                    self.show_error('2FA seeds are not supported in this version')
                    self.run('restore_from_seed')
                else:
                    self.load_2fa()
                    self.run('on_restore_seed', seed, is_ext)
            else:
                raise BaseException('Unknown seed type', seed_type)

    def on_restore_bip39(self, seed, passphrase):
        f = lambda x: self.run('on_bip44', seed, passphrase, int(x))
        self.account_id_dialog(f)

    def create_keystore(self, seed, passphrase):
        k = keystore.from_seed(seed, passphrase)
        self.on_keystore(k)

    def on_bip44(self, seed, passphrase, account_id):
        k = keystore.BIP32_KeyStore({})
        bip32_seed = keystore.bip39_to_seed(seed, passphrase)
        derivation = "m/44'/0'/%d'"%account_id
        k.add_xprv_from_seed(bip32_seed, derivation)
        self.on_keystore(k)

    def on_keystore(self, k):
        if self.wallet_type == 'standard':
            self.keystores.append(k)
            self.run('create_wallet')
        elif self.wallet_type == 'multisig':
            if k.xpub in map(lambda x: x.xpub, self.keystores):
                self.show_error(_('Error: duplicate master public key'))
                self.run('choose_keystore')
                return
            self.keystores.append(k)
            if len(self.keystores) == 1:
                xpub = k.get_master_public_key()
                self.stack = []
                self.run('show_xpub_and_add_cosigners', xpub)
            elif len(self.keystores) < self.n:
                self.run('choose_keystore')
            else:
                self.run('create_wallet')

    def create_wallet(self):
        if any(k.may_have_password() for k in self.keystores):
            self.request_password(run_next=self.on_password)
        else:
            self.on_password(None)

    def on_password(self, password):
        self.storage.put('use_encryption', bool(password))
        for k in self.keystores:
            if k.may_have_password():
                k.update_password(None, password)
        if self.wallet_type == 'standard':
            self.storage.put('keystore', k.dump())
            self.wallet = Standard_Wallet(self.storage)
            self.run('create_addresses')
        elif self.wallet_type == 'multisig':
            for i, k in enumerate(self.keystores):
                self.storage.put('x%d/'%(i+1), k.dump())
            self.storage.write()
            self.wallet = Multisig_Wallet(self.storage)
            self.run('create_addresses')

    def show_xpub_and_add_cosigners(self, xpub):
        self.show_xpub_dialog(xpub=xpub, run_next=lambda x: self.run('choose_keystore'))

    def add_cosigners(self, password, i):
        self.add_cosigner_dialog(run_next=lambda x: self.on_cosigner(x, password, i), index=i, is_valid=keystore.is_xpub)

    def on_cosigner(self, text, password, i):
        k = keystore.from_keys(text, password)
        self.on_keystore(k)

    def create_seed(self):
        from electrum.mnemonic import Mnemonic
        seed = Mnemonic('en').make_seed()
        self.opt_bip39 = False
        f = lambda x: self.request_passphrase(seed, x)
        self.show_seed_dialog(run_next=f, seed_text=seed)

    def request_passphrase(self, seed, opt_passphrase):
        if opt_passphrase:
            f = lambda x: self.confirm_seed(seed, x)
            self.passphrase_dialog(run_next=f)
        else:
            self.run('confirm_seed', seed, '')

    def confirm_seed(self, seed, passphrase):
        f = lambda x: self.confirm_passphrase(seed, passphrase)
        self.confirm_seed_dialog(run_next=f, test=lambda x: x==seed)

    def confirm_passphrase(self, seed, passphrase):
        f = lambda x: self.run('create_keystore', seed, x)
        if passphrase:
            title = _('Confirm Seed Extension')
            message = '\n'.join([
                _('Your seed extension must be saved together with your seed.'),
                _('Please type it here.'),
            ])
            self.line_dialog(run_next=f, title=title, message=message, default='', test=lambda x: x==passphrase)
        else:
            f('')

    def create_addresses(self):
        def task():
            self.wallet.synchronize()
            self.wallet.storage.write()
            self.terminate()
        msg = _("Electrum is generating your addresses, please wait.")
        self.waiting_dialog(task, msg)
Example #18
0
class BaseWizard(object):

    def __init__(self, config, network, path):
        super(BaseWizard, self).__init__()
        self.config = config
        self.network = network
        self.storage = WalletStorage(path)
        self.wallet = None
        self.stack = []
        self.plugin = None

    def run(self, *args):
        action = args[0]
        args = args[1:]
        self.stack.append((action, args))
        if not action:
            return
        if type(action) is tuple:
            self.plugin, action = action
        if self.plugin and hasattr(self.plugin, action):
            f = getattr(self.plugin, action)
            apply(f, (self,) + args)
        elif hasattr(self, action):
            f = getattr(self, action)
            apply(f, args)
        else:
            raise BaseException("unknown action", action)

    def can_go_back(self):
        return len(self.stack)>1

    def go_back(self):
        if not self.can_go_back():
            return
        self.stack.pop()
        action, args = self.stack.pop()
        self.run(action, *args)

    def new(self):
        name = os.path.basename(self.storage.path)
        title = _("Welcome to the Electrum installation wizard.")
        message = '\n'.join([
            _("The wallet '%s' does not exist.") % name,
            _("What kind of wallet do you want to create?")
        ])
        wallet_kinds = [
            ('standard',  _("Standard wallet")),
            ('twofactor', _("Wallet with two-factor authentication")),
            ('multisig',  _("Multi-signature wallet")),
        ]
        registered_kinds = Wallet.categories()
        choices = wallet_kinds#[pair for pair in wallet_kinds if pair[0] in registered_kinds]
        self.choice_dialog(title=title, message=message, choices=choices, run_next=self.on_wallet_type)

    def on_wallet_type(self, choice):
        self.wallet_type = choice
        if choice == 'standard':
            action = 'choose_seed'
        elif choice == 'multisig':
            action = 'choose_multisig'
        elif choice == 'twofactor':
            self.storage.put('wallet_type', '2fa')
            self.storage.put('use_trustedcoin', True)
            self.plugin = self.plugins.load_plugin('trustedcoin')
            action = self.storage.get_action()

        self.run(action)

    def choose_multisig(self):
        def on_multisig(m, n):
            self.multisig_type = "%dof%d"%(m, n)
            self.n = n
            self.run('choose_seed')
        self.multisig_dialog(run_next=on_multisig)

    def choose_seed(self):
        title = _('Seed and Private Keys')
        message = _('Do you want to create a new seed, or to restore a wallet using an existing seed?')
        if self.wallet_type in ['standard', 'multisig']:
            choices = [
                ('create_seed', _('Create a new seed')),
                ('restore_seed', _('I already have a seed')),
                ('restore_from_key', _('Import keys or addresses')),
                ('choose_hw',  _('Use hardware wallet')),
            ]
            self.choice_dialog(title=title, message=message, choices=choices, run_next=self.run)

    def restore_seed(self):
        # TODO: return derivation password too
        self.restore_seed_dialog(run_next=self.add_password, is_valid=keystore.is_seed)

    def on_restore(self, text):
        if keystore.is_address_list(text):
            self.wallet = Imported_Wallet(self.storage)
            for x in text.split():
                self.wallet.add_address(x)
            self.terminate()
        elif keystore.is_private(text):
            self.add_password(text)
        else:
            self.create_keystore(text, None)

    def restore_from_key(self):
        if self.wallet_type == 'standard':
            v = keystore.is_any_key
            title = _("Import keys")
            message = ' '.join([
                _("To create a watching-only wallet, please enter your master public key (xpub), or a list of Bitcoin addresses."),
                _("To create a spending wallet, please enter a master private key (xprv), or a list of Bitcoin private keys.")
            ])
        else:
            v = keystore.is_bip32_key
            title = _("Master public or private key")
            message = ' '.join([
                _("To create a watching-only wallet, please enter your master public key (xpub)."),
                _("To create a spending wallet, please enter a master private key (xprv).")
            ])
        self.restore_keys_dialog(title=title, message=message, run_next=self.on_restore, is_valid=v)

    def choose_hw(self):
        self.storage.put('key_type', 'hardware')
        hw_wallet_types, choices = self.plugins.hardware_wallets('create')
        choices = zip(hw_wallet_types, choices)
        title = _('Hardware wallet')
        if choices:
            msg = _('Select the type of hardware wallet: ')
        else:
            msg = ' '.join([
                _('No hardware wallet support found on your system.'),
                _('Please install the relevant libraries (eg python-trezor for Trezor).'),
            ])
        self.choice_dialog(title=title, message=msg, choices=choices, run_next=self.on_hardware)

    def on_hardware(self, hw_type):
        self.storage.put('hardware_type', hw_type)
        title = _('Hardware wallet') + ' [%s]' % hw_type
        message = _('Do you have a device, or do you want to restore a wallet using an existing seed?')
        choices = [
            ('on_hardware_device', _('I have a %s device')%hw_type),
            ('on_hardware_seed', _('I have a %s seed')%hw_type),
        ]
        self.choice_dialog(title=title, message=message, choices=choices, run_next=self.run)

    def on_hardware_device(self):
        from keystore import load_keystore
        keystore = load_keystore(self.storage, None)
        keystore.plugin.on_create_wallet(keystore, self)
        self.create_wallet(keystore, None)

    def on_hardware_seed(self):
        from keystore import load_keystore
        self.storage.put('key_type', 'hw_seed')
        keystore = load_keystore(self.storage, None)
        self.plugin = keystore #fixme .plugin
        keystore.on_restore_wallet(self)
        self.wallet = Standard_Wallet(self.storage)
        self.run('create_addresses')

    def create_wallet(self, k, password):
        if self.wallet_type == 'standard':
            k.save(self.storage, 'x/')
            self.wallet = Standard_Wallet(self.storage)
            self.run('create_addresses')
        elif self.wallet_type == 'multisig':
            self.storage.put('wallet_type', self.multisig_type)
            self.add_cosigner(k, 0)
            xpub = k.get_master_public_key()
            self.stack = []
            self.run('show_xpub_and_add_cosigners', password, xpub)

    def show_xpub_and_add_cosigners(self, password, xpub):
        self.show_xpub_dialog(xpub=xpub, run_next=lambda x: self.run('add_cosigners', password, 1))

    def add_cosigner(self, keystore, i):
        d = self.storage.get('master_public_keys', {})
        if keystore.xpub in d.values():
            raise BaseException('duplicate key')
        keystore.save(self.storage, 'x%d/'%(i+1))

    def add_cosigners(self, password, i):
        self.add_cosigner_dialog(run_next=lambda x: self.on_cosigner(x, password, i), index=i, is_valid=keystore.is_xpub)

    def on_cosigner(self, text, password, i):
        k = keystore.from_text(text, password)
        try:
            self.add_cosigner(k, i)
        except BaseException as e:
            self.show_message("error:" + str(e))
            return
        if i < self.n - 1:
            self.run('add_cosigners', password, i+1)
        else:
            self.wallet = Multisig_Wallet(self.storage)
            self.create_addresses()

    def create_seed(self):
        from electrum.mnemonic import Mnemonic
        seed = Mnemonic('en').make_seed()
        self.show_seed_dialog(run_next=self.confirm_seed, seed_text=seed)

    def confirm_seed(self, seed):
        self.confirm_seed_dialog(run_next=self.add_password, is_valid=lambda x: x==seed)

    def add_password(self, text):
        f = lambda pw: self.run('create_keystore', text, pw)
        self.request_password(run_next=f)

    def create_keystore(self, text, password):
        k = keystore.from_text(text, password)
        self.create_wallet(k, password)

    def create_addresses(self):
        def task():
            self.wallet.synchronize()
            self.wallet.storage.write()
            self.terminate()
        msg = _("Electrum is generating your addresses, please wait.")
        self.waiting_dialog(task, msg)
Example #19
0
class BaseWizard(object):
    def __init__(self, config, network, path):
        super(BaseWizard, self).__init__()
        self.config = config
        self.network = network
        self.storage = WalletStorage(path)
        self.wallet = None
        self.stack = []
        self.plugin = None

    def run(self, *args):
        action = args[0]
        args = args[1:]
        self.stack.append((action, args))
        if not action:
            return
        if type(action) is tuple:
            self.plugin, action = action
        if self.plugin and hasattr(self.plugin, action):
            f = getattr(self.plugin, action)
            apply(f, (self, ) + args)
        elif hasattr(self, action):
            f = getattr(self, action)
            apply(f, args)
        else:
            raise BaseException("unknown action", action)

    def can_go_back(self):
        return len(self.stack) > 1

    def go_back(self):
        if not self.can_go_back():
            return
        self.stack.pop()
        action, args = self.stack.pop()
        self.run(action, *args)

    def new(self):
        name = os.path.basename(self.storage.path)
        title = _("Welcome to the Electrum installation wizard.")
        message = '\n'.join([
            _("The wallet '%s' does not exist.") % name,
            _("What kind of wallet do you want to create?")
        ])
        wallet_kinds = [
            ('standard', _("Standard wallet")),
            ('twofactor', _("Wallet with two-factor authentication")),
            ('multisig', _("Multi-signature wallet")),
        ]
        registered_kinds = Wallet.categories()
        choices = wallet_kinds  #[pair for pair in wallet_kinds if pair[0] in registered_kinds]
        self.choice_dialog(title=title,
                           message=message,
                           choices=choices,
                           run_next=self.on_wallet_type)

    def on_wallet_type(self, choice):
        self.wallet_type = choice
        if choice == 'standard':
            action = 'choose_seed'
        elif choice == 'multisig':
            action = 'choose_multisig'
        elif choice == 'twofactor':
            self.storage.put('wallet_type', '2fa')
            self.storage.put('use_trustedcoin', True)
            self.plugin = self.plugins.load_plugin('trustedcoin')
            action = self.storage.get_action()

        self.run(action)

    def choose_multisig(self):
        def on_multisig(m, n):
            self.multisig_type = "%dof%d" % (m, n)
            self.n = n
            self.run('choose_seed')

        self.multisig_dialog(run_next=on_multisig)

    def choose_seed(self):
        title = _('Seed and Private Keys')
        message = _(
            'Do you want to create a new seed, or to restore a wallet using an existing seed?'
        )
        if self.wallet_type in ['standard', 'multisig']:
            choices = [
                ('create_seed', _('Create a new seed')),
                ('restore_seed', _('I already have a seed')),
                ('restore_from_key', _('Import keys or addresses')),
                ('choose_hw', _('Use hardware wallet')),
            ]
            self.choice_dialog(title=title,
                               message=message,
                               choices=choices,
                               run_next=self.run)

    def restore_seed(self):
        # TODO: return derivation password too
        self.restore_seed_dialog(run_next=self.add_password,
                                 is_valid=keystore.is_seed)

    def on_restore(self, text):
        if keystore.is_address_list(text):
            self.wallet = Imported_Wallet(self.storage)
            for x in text.split():
                self.wallet.add_address(x)
            self.terminate()
        elif keystore.is_private(text):
            self.add_password(text)
        else:
            self.create_keystore(text, None)

    def restore_from_key(self):
        if self.wallet_type == 'standard':
            v = keystore.is_any_key
            title = _("Import keys")
            message = ' '.join([
                _("To create a watching-only wallet, please enter your master public key (xpub), or a list of Bitcoin addresses."
                  ),
                _("To create a spending wallet, please enter a master private key (xprv), or a list of Bitcoin private keys."
                  )
            ])
        else:
            v = keystore.is_bip32_key
            title = _("Master public or private key")
            message = ' '.join([
                _("To create a watching-only wallet, please enter your master public key (xpub)."
                  ),
                _("To create a spending wallet, please enter a master private key (xprv)."
                  )
            ])
        self.restore_keys_dialog(title=title,
                                 message=message,
                                 run_next=self.on_restore,
                                 is_valid=v)

    def choose_hw(self):
        self.storage.put('key_type', 'hardware')
        hw_wallet_types, choices = self.plugins.hardware_wallets('create')
        choices = zip(hw_wallet_types, choices)
        title = _('Hardware wallet')
        if choices:
            msg = _('Select the type of hardware wallet: ')
        else:
            msg = ' '.join([
                _('No hardware wallet support found on your system.'),
                _('Please install the relevant libraries (eg python-trezor for Trezor).'
                  ),
            ])
        self.choice_dialog(title=title,
                           message=msg,
                           choices=choices,
                           run_next=self.on_hardware)

    def on_hardware(self, hw_type):
        self.storage.put('hardware_type', hw_type)
        title = _('Hardware wallet') + ' [%s]' % hw_type
        message = _(
            'Do you have a device, or do you want to restore a wallet using an existing seed?'
        )
        choices = [
            ('on_hardware_device', _('I have a %s device') % hw_type),
            ('on_hardware_seed', _('I have a %s seed') % hw_type),
        ]
        self.choice_dialog(title=title,
                           message=message,
                           choices=choices,
                           run_next=self.run)

    def on_hardware_device(self):
        from keystore import load_keystore
        keystore = load_keystore(self.storage, None)
        keystore.plugin.on_create_wallet(keystore, self)
        self.create_wallet(keystore, None)

    def on_hardware_seed(self):
        from keystore import load_keystore
        self.storage.put('key_type', 'hw_seed')
        keystore = load_keystore(self.storage, None)
        self.plugin = keystore  #fixme .plugin
        keystore.on_restore_wallet(self)
        self.wallet = Standard_Wallet(self.storage)
        self.run('create_addresses')

    def create_wallet(self, k, password):
        if self.wallet_type == 'standard':
            k.save(self.storage, 'x/')
            self.wallet = Standard_Wallet(self.storage)
            self.run('create_addresses')
        elif self.wallet_type == 'multisig':
            self.storage.put('wallet_type', self.multisig_type)
            self.add_cosigner(k, 0)
            xpub = k.get_master_public_key()
            self.stack = []
            self.run('show_xpub_and_add_cosigners', password, xpub)

    def show_xpub_and_add_cosigners(self, password, xpub):
        self.show_xpub_dialog(
            xpub=xpub,
            run_next=lambda x: self.run('add_cosigners', password, 1))

    def add_cosigner(self, keystore, i):
        d = self.storage.get('master_public_keys', {})
        if keystore.xpub in d.values():
            raise BaseException('duplicate key')
        keystore.save(self.storage, 'x%d/' % (i + 1))

    def add_cosigners(self, password, i):
        self.add_cosigner_dialog(
            run_next=lambda x: self.on_cosigner(x, password, i),
            index=i,
            is_valid=keystore.is_xpub)

    def on_cosigner(self, text, password, i):
        k = keystore.from_text(text, password)
        try:
            self.add_cosigner(k, i)
        except BaseException as e:
            self.show_message("error:" + str(e))
            return
        if i < self.n - 1:
            self.run('add_cosigners', password, i + 1)
        else:
            self.wallet = Multisig_Wallet(self.storage)
            self.create_addresses()

    def create_seed(self):
        from electrum.mnemonic import Mnemonic
        seed = Mnemonic('en').make_seed()
        self.show_seed_dialog(run_next=self.confirm_seed, seed_text=seed)

    def confirm_seed(self, seed):
        self.confirm_seed_dialog(run_next=self.add_password,
                                 is_valid=lambda x: x == seed)

    def add_password(self, text):
        f = lambda pw: self.run('create_keystore', text, pw)
        self.request_password(run_next=f)

    def create_keystore(self, text, password):
        k = keystore.from_text(text, password)
        self.create_wallet(k, password)

    def create_addresses(self):
        def task():
            self.wallet.synchronize()
            self.wallet.storage.write()
            self.terminate()

        msg = _("Electrum is generating your addresses, please wait.")
        self.waiting_dialog(task, msg)
Example #20
0
class BaseWizard(object):
    def __init__(self, config, network, path):
        super(BaseWizard, self).__init__()
        self.config = config
        self.network = network
        self.storage = WalletStorage(path)
        self.wallet = None
        self.stack = []
        self.plugin = None

    def run(self, *args):
        action = args[0]
        args = args[1:]
        self.stack.append((action, args))
        if not action:
            return
        if type(action) is tuple:
            self.plugin, action = action
        if self.plugin and hasattr(self.plugin, action):
            f = getattr(self.plugin, action)
            apply(f, (self, ) + args)
        elif hasattr(self, action):
            f = getattr(self, action)
            apply(f, args)
        else:
            raise BaseException("unknown action", action)

    def can_go_back(self):
        return len(self.stack) > 1

    def go_back(self):
        if not self.can_go_back():
            return
        self.stack.pop()
        action, args = self.stack.pop()
        self.run(action, *args)

    def new(self):
        name = os.path.basename(self.storage.path)
        title = _("Welcome to the Electrum installation wizard.")
        message = '\n'.join([
            _("The wallet '%s' does not exist.") % name,
            _("What kind of wallet do you want to create?")
        ])
        wallet_kinds = [
            ('standard', _("Standard wallet")),
            ('2fa', _("Wallet with two-factor authentication")),
            ('multisig', _("Multi-signature wallet")),
            ('imported', _("Watch Bitcoin addresses")),
        ]
        choices = [pair for pair in wallet_kinds if pair[0] in wallet_types]
        self.choice_dialog(title=title,
                           message=message,
                           choices=choices,
                           run_next=self.on_wallet_type)

    def on_wallet_type(self, choice):
        self.wallet_type = choice
        if choice == 'standard':
            action = 'choose_keystore'
        elif choice == 'multisig':
            action = 'choose_multisig'
        elif choice == '2fa':
            self.storage.put('wallet_type', '2fa')
            self.storage.put('use_trustedcoin', True)
            self.plugin = self.plugins.load_plugin('trustedcoin')
            action = self.storage.get_action()
        elif choice == 'imported':
            action = 'import_addresses'
        self.run(action)

    def choose_multisig(self):
        def on_multisig(m, n):
            self.multisig_type = "%dof%d" % (m, n)
            self.storage.put('wallet_type', self.multisig_type)
            self.n = n
            self.keystores = []
            self.run('choose_keystore')

        self.multisig_dialog(run_next=on_multisig)

    def choose_keystore(self):
        assert self.wallet_type in ['standard', 'multisig']
        c = self.wallet_type == 'multisig' and len(self.keystores) > 0
        title = _('Add cosigner') + ' %d' % len(self.keystores) if c else _(
            'Keystore')
        message = _(
            'Do you want to create a new seed, or to restore a wallet using an existing seed?'
        )
        if not c:
            choices = [
                ('create_seed', _('Create a new seed')),
                ('restore_seed', _('I already have a seed')),
                ('restore_from_key', _('Import keys')),
                ('choose_device', _('Use hardware device')),
            ]
        else:
            choices = [
                ('restore_from_key', _('Import cosigner key')),
                ('choose_device', _('Cosign with hardware device')),
            ]

        self.choice_dialog(title=title,
                           message=message,
                           choices=choices,
                           run_next=self.run)

    def restore_seed(self):
        # TODO: return derivation password too
        self.restore_seed_dialog(run_next=self.add_password,
                                 is_valid=keystore.is_seed)

    def on_restore(self, text):
        if keystore.is_address_list(text):
            self.wallet = Imported_Wallet(self.storage)
            for x in text.split():
                self.wallet.import_address(x)
            self.terminate()
        elif keystore.is_private(text):
            self.add_password(text)
        else:
            self.create_keystore(text, None)

    def import_addresses(self):
        v = keystore.is_address_list
        title = _("Import Bitcoin Addresses")
        message = _(
            "Enter a list of Bitcoin addresses. This will create a watching-only wallet."
        )
        self.restore_keys_dialog(title=title,
                                 message=message,
                                 run_next=self.on_restore,
                                 is_valid=v)

    def restore_from_key(self):
        if self.wallet_type == 'standard':
            v = keystore.is_any_key
            title = _("Import keys")
            message = ' '.join([
                _("To create a watching-only wallet, please enter your master public key (xpub)."
                  ),
                _("To create a spending wallet, please enter a master private key (xprv), or a list of Bitcoin private keys."
                  )
            ])
        else:
            v = keystore.is_bip32_key
            title = _("Master public or private key")
            message = ' '.join([
                _("To create a watching-only wallet, please enter your master public key (xpub)."
                  ),
                _("To create a spending wallet, please enter a master private key (xprv)."
                  )
            ])
        self.restore_keys_dialog(title=title,
                                 message=message,
                                 run_next=self.on_restore,
                                 is_valid=v)

    def choose_device(self):
        title = _('Hardware Keystore')
        # check available plugins
        support = self.plugins.get_hardware_support()
        if not support:
            msg = '\n'.join([
                _('No hardware wallet support found on your system.'),
                _('Please install the relevant libraries (eg python-trezor for Trezor).'
                  ),
            ])
            self.confirm_dialog(title=title,
                                message=msg,
                                run_next=lambda x: self.choose_device())
            return
        # scan devices
        devices = []
        for name, description, plugin in support:
            devmgr = plugin.device_manager()
            try:
                u = devmgr.unpaired_device_infos(self, plugin)
            except:
                print "error", name
                continue
            devices += map(lambda x: (name, x), u)
        if not devices:
            msg = '\n'.join([
                _('No hardware device detected.'),
                _('To trigger a rescan, press \'next\'.'),
            ])
            self.confirm_dialog(title=title,
                                message=msg,
                                run_next=lambda x: self.choose_device())
            return
        # select device
        self.devices = devices
        choices = []
        for name, device_info in devices:
            choices.append(((name, device_info), device_info.description))
        msg = _('Select a device') + ':'
        self.choice_dialog(title=title,
                           message=msg,
                           choices=choices,
                           run_next=self.on_device)

    def on_device(self, name, device_info):
        f = lambda x: self.run('on_hardware_account_id', name, device_info, x)
        self.account_id_dialog(run_next=f)

    def on_hardware_account_id(self, hw_type, device_info, account_id):
        from keystore import hardware_keystore, bip44_derivation
        derivation = bip44_derivation(int(account_id))
        plugin = self.plugins.get_plugin(hw_type)
        xpub = plugin.setup_device(device_info, derivation, self)
        # create keystore
        d = {
            'type': 'hardware',
            'hw_type': hw_type,
            'derivation': derivation,
            'xpub': xpub,
        }
        k = hardware_keystore(hw_type, d)
        self.on_keystore(k, None)

    def on_hardware_seed(self):
        self.storage.put('key_type', 'hw_seed')
        is_valid = lambda x: True  #fixme: bip39
        f = lambda seed: self.run('on_bip39_seed', seed)
        self.restore_seed_dialog(run_next=f, is_valid=is_valid)

    def on_bip39_seed(self, seed):
        f = lambda passphrase: self.run('on_bip39_passphrase', seed, passphrase
                                        )
        self.request_passphrase(self.storage.get('hw_type'), run_next=f)

    def on_bip39_passphrase(self, seed, passphrase):
        f = lambda account_id: self.run('on_bip44_account_id', seed,
                                        passphrase, account_id)
        self.account_id_dialog(run_next=f)

    def on_bip44_account_id(self, seed, passphrase, account_id):
        f = lambda pw: self.run('on_bip44', seed, passphrase, account_id, pw)
        self.request_password(run_next=f)

    def on_bip44(self, seed, passphrase, account_id, password):
        import keystore
        k = keystore.BIP32_KeyStore()
        k.add_seed(seed, password)
        bip32_seed = keystore.bip39_to_seed(seed, passphrase)
        derivation = "m/44'/0'/%d'" % account_id
        self.storage.put('account_id', account_id)
        k.add_xprv_from_seed(bip32_seed, derivation, password)
        self.on_keystore(k, password)

    def on_keystore(self, k, password):
        if self.wallet_type == 'standard':
            self.storage.put('keystore', k.dump())
            self.wallet = Standard_Wallet(self.storage)
            self.run('create_addresses')
        elif self.wallet_type == 'multisig':

            if k.xpub in map(lambda x: x.xpub, self.keystores):
                raise BaseException('duplicate key')
            self.keystores.append(k)

            if len(self.keystores) == 1:
                xpub = k.get_master_public_key()
                self.stack = []
                self.run('show_xpub_and_add_cosigners', xpub)
            elif len(self.keystores) < self.n:
                self.run('choose_keystore')
            else:
                for i, k in enumerate(self.keystores):
                    self.storage.put('x%d/' % (i + 1), k.dump())
                self.storage.write()
                self.wallet = Multisig_Wallet(self.storage)
                self.run('create_addresses')

    def show_xpub_and_add_cosigners(self, xpub):
        self.show_xpub_dialog(xpub=xpub,
                              run_next=lambda x: self.run('choose_keystore'))

    def add_cosigners(self, password, i):
        self.add_cosigner_dialog(
            run_next=lambda x: self.on_cosigner(x, password, i),
            index=i,
            is_valid=keystore.is_xpub)

    def on_cosigner(self, text, password, i):
        k = keystore.from_text(text, password)
        self.on_keystore(k)

    def create_seed(self):
        from electrum.mnemonic import Mnemonic
        seed = Mnemonic('en').make_seed()
        self.show_seed_dialog(run_next=self.confirm_seed, seed_text=seed)

    def confirm_seed(self, seed):
        self.confirm_seed_dialog(run_next=self.add_password,
                                 is_valid=lambda x: x == seed)

    def add_password(self, text):
        f = lambda pw: self.run('create_keystore', text, pw)
        self.request_password(run_next=f)

    def create_keystore(self, text, password):
        k = keystore.from_text(text, password)
        self.on_keystore(k, password)

    def create_addresses(self):
        def task():
            self.wallet.synchronize()
            self.wallet.storage.write()
            self.terminate()

        msg = _("Electrum is generating your addresses, please wait.")
        self.waiting_dialog(task, msg)