2017-02-04 06:48:13 -08:00
# -*- coding: utf-8 -*-
2016-06-16 10:25:44 -07:00
#
# 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
2017-12-07 02:35:10 -08:00
import sys
import traceback
2017-01-22 10:25:24 -08:00
from . import bitcoin
from . import keystore
from . keystore import bip44_derivation
2017-11-12 20:54:04 -08:00
from . wallet import Imported_Wallet , Standard_Wallet , Multisig_Wallet , wallet_types
2017-12-07 02:35:10 -08:00
from . storage import STO_EV_USER_PW , STO_EV_XPUB_PW , get_derivation_used_for_hw_device_encryption
2017-01-22 10:25:24 -08:00
from . i18n import _
2017-12-07 02:35:10 -08:00
from . util import UserCancelled
2016-07-01 02:44:26 -07:00
2017-12-07 02:35:10 -08:00
# hardware device setup purpose
HWD_SETUP_NEW_WALLET , HWD_SETUP_DECRYPT_WALLET = range ( 0 , 2 )
2017-02-04 06:48:13 -08:00
2018-01-09 12:10:11 -08:00
class ScriptTypeNotSupported ( Exception ) : pass
2016-06-16 10:25:44 -07:00
class BaseWizard ( object ) :
2017-03-05 04:30:57 -08:00
def __init__ ( self , config , storage ) :
2016-06-16 10:25:44 -07:00
super ( BaseWizard , self ) . __init__ ( )
2016-07-01 23:58:56 -07:00
self . config = config
2017-03-05 04:30:57 -08:00
self . storage = storage
2016-06-16 10:25:44 -07:00
self . wallet = None
2016-06-20 07:25:11 -07:00
self . stack = [ ]
2016-07-01 23:58:56 -07:00
self . plugin = None
2016-08-24 00:13:21 -07:00
self . keystores = [ ]
2016-08-29 06:33:16 -07:00
self . is_kivy = config . get ( ' gui ' ) == ' kivy '
2017-01-16 00:48:38 -08:00
self . seed_type = None
2016-06-16 10:25:44 -07:00
2016-07-30 06:04:15 -07:00
def run ( self , * args ) :
action = args [ 0 ]
args = args [ 1 : ]
2016-06-20 07:25:11 -07:00
self . stack . append ( ( action , args ) )
2016-06-16 10:25:44 -07:00
if not action :
return
2016-07-01 23:58:56 -07:00
if type ( action ) is tuple :
self . plugin , action = action
if self . plugin and hasattr ( self . plugin , action ) :
f = getattr ( self . plugin , action )
2017-07-31 20:22:18 -07:00
f ( self , * args )
2016-06-20 07:25:11 -07:00
elif hasattr ( self , action ) :
2016-06-16 10:25:44 -07:00
f = getattr ( self , action )
2017-07-31 20:22:18 -07:00
f ( * args )
2016-06-16 10:25:44 -07:00
else :
raise BaseException ( " unknown action " , action )
2016-06-20 07:25:11 -07:00
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 )
2016-06-16 10:25:44 -07:00
def new ( self ) :
name = os . path . basename ( self . storage . path )
2017-03-15 04:13:20 -07:00
title = _ ( " Create " ) + ' ' + name
2016-06-20 07:25:11 -07:00
message = ' \n ' . join ( [
2016-06-16 10:25:44 -07:00
_ ( " What kind of wallet do you want to create? " )
] )
2016-06-20 07:25:11 -07:00
wallet_kinds = [
( ' standard ' , _ ( " Standard wallet " ) ) ,
2016-08-19 08:26:57 -07:00
( ' 2fa ' , _ ( " Wallet with two-factor authentication " ) ) ,
2016-06-20 07:25:11 -07:00
( ' multisig ' , _ ( " Multi-signature wallet " ) ) ,
2017-09-25 12:35:14 -07:00
( ' imported ' , _ ( " Import Bitcoin addresses or private keys " ) ) ,
2016-06-16 10:25:44 -07:00
]
2016-08-19 08:26:57 -07:00
choices = [ pair for pair in wallet_kinds if pair [ 0 ] in wallet_types ]
2016-07-01 23:58:56 -07:00
self . choice_dialog ( title = title , message = message , choices = choices , run_next = self . on_wallet_type )
2016-06-20 07:25:11 -07:00
2016-09-27 21:30:00 -07:00
def load_2fa ( self ) :
self . storage . put ( ' wallet_type ' , ' 2fa ' )
self . storage . put ( ' use_trustedcoin ' , True )
self . plugin = self . plugins . load_plugin ( ' trustedcoin ' )
2016-06-20 07:25:11 -07:00
def on_wallet_type ( self , choice ) :
self . wallet_type = choice
if choice == ' standard ' :
2016-08-19 02:47:07 -07:00
action = ' choose_keystore '
2016-06-20 07:25:11 -07:00
elif choice == ' multisig ' :
action = ' choose_multisig '
2016-08-19 08:26:57 -07:00
elif choice == ' 2fa ' :
2016-09-27 21:30:00 -07:00
self . load_2fa ( )
2016-07-01 23:58:56 -07:00
action = self . storage . get_action ( )
2016-08-22 03:50:24 -07:00
elif choice == ' imported ' :
2017-09-25 12:35:14 -07:00
action = ' import_addresses_or_keys '
2016-06-20 07:25:11 -07:00
self . run ( action )
def choose_multisig ( self ) :
def on_multisig ( m , n ) :
self . multisig_type = " %d of %d " % ( m , n )
2016-08-22 03:50:24 -07:00
self . storage . put ( ' wallet_type ' , self . multisig_type )
2016-07-01 23:58:56 -07:00
self . n = n
2016-08-19 02:47:07 -07:00
self . run ( ' choose_keystore ' )
2016-06-20 07:25:11 -07:00
self . multisig_dialog ( run_next = on_multisig )
2016-06-16 10:25:44 -07:00
2016-08-19 02:47:07 -07:00
def choose_keystore ( self ) :
2016-08-19 05:45:52 -07:00
assert self . wallet_type in [ ' standard ' , ' multisig ' ]
2016-08-24 00:13:21 -07:00
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? ' )
2016-08-22 03:50:24 -07:00
choices = [
2017-09-14 03:20:11 -07:00
( ' choose_seed_type ' , _ ( ' Create a new seed ' ) ) ,
2016-08-24 21:43:27 -07:00
( ' restore_from_seed ' , _ ( ' I already have a seed ' ) ) ,
2016-08-28 02:29:16 -07:00
( ' restore_from_key ' , _ ( ' Use public or private keys ' ) ) ,
2016-08-22 03:50:24 -07:00
]
2016-08-30 02:19:30 -07:00
if not self . is_kivy :
choices . append ( ( ' choose_hw_device ' , _ ( ' Use a hardware device ' ) ) )
2016-08-22 03:50:24 -07:00
else :
2016-08-24 00:13:21 -07:00
message = _ ( ' Add a cosigner to your multi-sig wallet ' )
2016-08-22 03:50:24 -07:00
choices = [
2016-08-28 02:29:16 -07:00
( ' restore_from_key ' , _ ( ' Enter cosigner key ' ) ) ,
2016-09-28 08:03:02 -07:00
( ' restore_from_seed ' , _ ( ' Enter cosigner seed ' ) ) ,
2016-08-22 03:50:24 -07:00
]
2016-08-30 02:19:30 -07:00
if not self . is_kivy :
choices . append ( ( ' choose_hw_device ' , _ ( ' Cosign with hardware device ' ) ) )
2016-08-22 03:50:24 -07:00
2016-08-19 05:45:52 -07:00
self . choice_dialog ( title = title , message = message , choices = choices , run_next = self . run )
2016-06-16 10:25:44 -07:00
2017-09-25 12:35:14 -07:00
def import_addresses_or_keys ( self ) :
v = lambda x : keystore . is_address_list ( x ) or keystore . is_private_key_list ( x )
2016-08-22 03:50:24 -07:00
title = _ ( " Import Bitcoin Addresses " )
2017-09-25 12:35:14 -07:00
message = _ ( " Enter a list of Bitcoin addresses (this will create a watching-only wallet), or a list of private keys. " )
2017-12-23 18:30:04 -08:00
self . add_xpub_dialog ( title = title , message = message , run_next = self . on_import ,
is_valid = v , allow_multi = True )
2017-09-25 12:35:14 -07:00
def on_import ( self , text ) :
2017-12-07 02:35:10 -08:00
# create a temporary wallet and exploit that modifications
# will be reflected on self.storage
2017-09-25 12:35:14 -07:00
if keystore . is_address_list ( text ) :
2017-12-07 02:35:10 -08:00
w = Imported_Wallet ( self . storage )
2017-09-25 12:35:14 -07:00
for x in text . split ( ) :
2017-12-07 02:35:10 -08:00
w . import_address ( x )
2017-09-25 12:35:14 -07:00
elif keystore . is_private_key_list ( text ) :
k = keystore . Imported_KeyStore ( { } )
self . storage . put ( ' keystore ' , k . dump ( ) )
2017-12-07 02:35:10 -08:00
w = Imported_Wallet ( self . storage )
2017-09-25 12:35:14 -07:00
for x in text . split ( ) :
2017-12-07 02:35:10 -08:00
w . import_private_key ( x , None )
self . keystores . append ( w . keystore )
else :
return self . terminate ( )
return self . run ( ' create_wallet ' )
2016-08-22 03:50:24 -07:00
2016-07-01 02:44:26 -07:00
def restore_from_key ( self ) :
if self . wallet_type == ' standard ' :
2017-09-25 12:35:14 -07:00
v = keystore . is_master_key
title = _ ( " Create keystore from a master key " )
2016-07-01 02:44:26 -07:00
message = ' ' . join ( [
2017-09-25 12:35:14 -07:00
_ ( " To create a watching-only wallet, please enter your master public key (xpub/ypub/zpub). " ) ,
_ ( " To create a spending wallet, please enter a master private key (xprv/yprv/zprv). " )
2016-07-01 02:44:26 -07:00
] )
2016-09-26 03:02:54 -07:00
self . add_xpub_dialog ( title = title , message = message , run_next = self . on_restore_from_key , is_valid = v )
2016-07-01 02:44:26 -07:00
else :
2016-09-26 03:02:54 -07:00
i = len ( self . keystores ) + 1
2017-09-14 05:38:19 -07:00
self . add_cosigner_dialog ( index = i , run_next = self . on_restore_from_key , is_valid = keystore . is_bip32_key )
2016-08-24 21:43:27 -07:00
def on_restore_from_key ( self , text ) :
2017-09-25 12:35:14 -07:00
k = keystore . from_master_key ( text )
2016-08-25 03:18:51 -07:00
self . on_keystore ( k )
2016-06-20 07:25:11 -07:00
2017-12-07 02:35:10 -08:00
def choose_hw_device ( self , purpose = HWD_SETUP_NEW_WALLET ) :
2016-08-19 02:47:07 -07:00
title = _ ( ' Hardware Keystore ' )
2016-08-23 04:40:11 -07:00
# check available plugins
support = self . plugins . get_hardware_support ( )
if not support :
msg = ' \n ' . join ( [
2016-06-20 07:25:11 -07:00
_ ( ' No hardware wallet support found on your system. ' ) ,
_ ( ' Please install the relevant libraries (eg python-trezor for Trezor). ' ) ,
] )
2017-12-07 02:35:10 -08:00
self . confirm_dialog ( title = title , message = msg , run_next = lambda x : self . choose_hw_device ( purpose ) )
2016-08-23 04:40:11 -07:00
return
# scan devices
devices = [ ]
2016-08-23 20:58:41 -07:00
devmgr = self . plugins . device_manager
2016-08-23 04:40:11 -07:00
for name , description , plugin in support :
try :
2016-08-23 20:58:41 -07:00
# FIXME: side-effect: unpaired_device_info sets client.handler
u = devmgr . unpaired_device_infos ( None , plugin )
2016-08-23 04:40:11 -07:00
except :
2016-08-23 20:58:41 -07:00
devmgr . print_error ( " error " , name )
2016-08-23 04:40:11 -07:00
continue
2017-02-05 02:38:44 -08:00
devices + = list ( map ( lambda x : ( name , x ) , u ) )
2016-08-23 04:40:11 -07:00
if not devices :
2017-03-21 02:07:31 -07:00
msg = ' ' . join ( [
_ ( ' No hardware device detected. ' ) + ' \n ' ,
_ ( ' To trigger a rescan, press \' Next \' . ' ) + ' \n \n ' ,
_ ( ' If your device is not detected on Windows, go to " Settings " , " Devices " , " Connected devices " , and do " Remove device " . Then, plug your device again. ' ) + ' ' ,
_ ( ' On Linux, you might have to add a new permission to your udev rules. ' ) ,
2016-08-23 04:40:11 -07:00
] )
2017-12-07 02:35:10 -08:00
self . confirm_dialog ( title = title , message = msg , run_next = lambda x : self . choose_hw_device ( purpose ) )
2016-08-23 04:40:11 -07:00
return
# select device
self . devices = devices
choices = [ ]
2016-08-25 06:31:21 -07:00
for name , info in devices :
state = _ ( " initialized " ) if info . initialized else _ ( " wiped " )
2018-02-03 22:26:55 -08:00
label = info . label or _ ( " An unnamed {} " ) . format ( name )
2016-08-25 06:31:21 -07:00
descr = " %s [ %s , %s ] " % ( label , name , state )
choices . append ( ( ( name , info ) , descr ) )
2016-08-23 04:40:11 -07:00
msg = _ ( ' Select a device ' ) + ' : '
2017-12-07 02:35:10 -08:00
self . choice_dialog ( title = title , message = msg , choices = choices , run_next = lambda * args : self . on_device ( * args , purpose = purpose ) )
2016-08-23 04:40:11 -07:00
2017-12-07 02:35:10 -08:00
def on_device ( self , name , device_info , * , purpose ) :
2016-08-24 01:47:27 -07:00
self . plugin = self . plugins . get_plugin ( name )
2016-10-19 23:32:44 -07:00
try :
2017-12-07 02:35:10 -08:00
self . plugin . setup_device ( device_info , self , purpose )
2018-02-04 12:59:58 -08:00
except OSError as e :
self . show_error ( _ ( ' We encountered an error while connecting to your device: ' )
+ ' \n ' + str ( e ) + ' \n '
+ _ ( ' To try to fix this, we will now re-pair with your device. ' ) + ' \n '
+ _ ( ' Please try again. ' ) )
devmgr = self . plugins . device_manager
devmgr . unpair_id ( device_info . device . id_ )
self . choose_hw_device ( purpose )
return
2016-10-19 23:32:44 -07:00
except BaseException as e :
self . show_error ( str ( e ) )
2017-12-07 02:35:10 -08:00
self . choose_hw_device ( purpose )
2016-10-19 23:32:44 -07:00
return
2017-12-07 02:35:10 -08:00
if purpose == HWD_SETUP_NEW_WALLET :
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 :
f = lambda x : self . run ( ' on_hw_derivation ' , name , device_info , str ( x ) )
self . derivation_dialog ( f )
elif purpose == HWD_SETUP_DECRYPT_WALLET :
derivation = get_derivation_used_for_hw_device_encryption ( )
xpub = self . plugin . get_xpub ( device_info . device . id_ , derivation , ' standard ' , self )
password = keystore . Xpub . get_pubkey_from_xpub ( xpub , ( ) )
self . storage . decrypt ( password )
2016-09-23 10:00:42 -07:00
else :
2017-12-07 02:35:10 -08:00
raise Exception ( ' unknown purpose: %s ' % purpose )
2016-08-30 00:51:53 -07:00
2017-06-20 01:47:02 -07:00
def derivation_dialog ( self , f ) :
2017-12-30 17:20:58 -08:00
default = bip44_derivation ( 0 , bip43_purpose = 44 )
2016-08-30 00:51:53 -07:00
message = ' \n ' . join ( [
2017-06-20 01:47:02 -07:00
_ ( ' Enter your wallet derivation here. ' ) ,
_ ( ' If you are not sure what this is, leave this field unchanged. ' )
2016-08-30 00:51:53 -07:00
] )
2017-12-27 08:01:47 -08:00
presets = (
2017-12-30 17:20:58 -08:00
( ' legacy BIP44 ' , bip44_derivation ( 0 , bip43_purpose = 44 ) ) ,
( ' p2sh-segwit BIP49 ' , bip44_derivation ( 0 , bip43_purpose = 49 ) ) ,
( ' native-segwit BIP84 ' , bip44_derivation ( 0 , bip43_purpose = 84 ) ) ,
2017-12-27 08:01:47 -08:00
)
2018-01-09 12:10:11 -08:00
while True :
try :
self . line_dialog ( run_next = f , title = _ ( ' Derivation ' ) , message = message ,
default = default , test = bitcoin . is_bip32_derivation ,
presets = presets )
return
except ScriptTypeNotSupported as e :
self . show_error ( e )
# let the user choose again
2016-08-15 02:48:33 -07:00
2016-09-23 10:00:42 -07:00
def on_hw_derivation ( self , name , device_info , derivation ) :
2017-02-05 02:38:44 -08:00
from . keystore import hardware_keystore
2017-12-30 17:20:58 -08:00
xtype = keystore . xtype_from_derivation ( derivation )
2017-11-03 02:32:16 -07:00
try :
xpub = self . plugin . get_xpub ( device_info . device . id_ , derivation , xtype , self )
2018-01-09 12:10:11 -08:00
except ScriptTypeNotSupported :
raise # this is handled in derivation_dialog
2017-11-03 02:32:16 -07:00
except BaseException as e :
self . show_error ( e )
2016-08-28 23:47:48 -07:00
return
2016-08-23 00:21:24 -07:00
d = {
' type ' : ' hardware ' ,
2016-08-23 23:52:21 -07:00
' hw_type ' : name ,
2016-08-23 00:21:24 -07:00
' derivation ' : derivation ,
' xpub ' : xpub ,
2016-08-26 02:45:12 -07:00
' label ' : device_info . label ,
2016-08-23 00:21:24 -07:00
}
2016-08-31 00:35:27 -07:00
k = hardware_keystore ( d )
2016-08-25 03:42:00 -07:00
self . on_keystore ( k )
2016-07-01 23:58:56 -07:00
2016-09-27 21:30:00 -07:00
def passphrase_dialog ( self , run_next ) :
2016-10-10 08:11:46 -07:00
title = _ ( ' Seed extension ' )
2016-09-27 21:30:00 -07:00
message = ' \n ' . join ( [
2016-10-10 08:11:46 -07:00
_ ( ' You may extend your seed with custom words. ' ) ,
_ ( ' Your seed extension must be saved together with your seed. ' ) ,
2016-09-27 21:30:00 -07:00
] )
warning = ' \n ' . join ( [
_ ( ' Note that this is NOT your encryption password. ' ) ,
_ ( ' If you do not know what this is, leave this field empty. ' ) ,
] )
2016-10-10 08:11:46 -07:00
self . line_dialog ( title = title , message = message , warning = warning , default = ' ' , test = lambda x : True , run_next = run_next )
2016-08-30 00:51:53 -07:00
2016-09-27 21:30:00 -07:00
def restore_from_seed ( self ) :
2016-09-28 00:53:17 -07:00
self . opt_bip39 = True
2016-09-29 16:15:28 -07:00
self . opt_ext = True
2017-10-22 21:42:34 -07:00
is_cosigning_seed = lambda x : bitcoin . seed_type ( x ) in [ ' standard ' , ' segwit ' ]
test = bitcoin . is_seed if self . wallet_type == ' standard ' else is_cosigning_seed
2016-09-27 21:30:00 -07:00
self . restore_seed_dialog ( run_next = self . on_restore_seed , test = test )
2016-08-15 02:48:33 -07:00
2016-09-29 16:15:28 -07:00
def on_restore_seed ( self , seed , is_bip39 , is_ext ) :
2017-01-16 00:48:38 -08:00
self . seed_type = ' bip39 ' if is_bip39 else bitcoin . seed_type ( seed )
if self . seed_type == ' bip39 ' :
2016-09-29 16:15:28 -07:00
f = lambda passphrase : self . on_restore_bip39 ( seed , passphrase )
self . passphrase_dialog ( run_next = f ) if is_ext else f ( ' ' )
2017-01-16 00:48:38 -08:00
elif self . seed_type in [ ' standard ' , ' segwit ' ] :
f = lambda passphrase : self . run ( ' create_keystore ' , seed , passphrase )
self . passphrase_dialog ( run_next = f ) if is_ext else f ( ' ' )
elif self . seed_type == ' old ' :
self . run ( ' create_keystore ' , seed , ' ' )
elif self . seed_type == ' 2fa ' :
if self . is_kivy :
2018-01-05 04:42:06 -08:00
self . show_error ( _ ( ' 2FA seeds are not supported in this version ' ) )
2017-01-16 00:48:38 -08:00
self . run ( ' restore_from_seed ' )
2016-09-27 21:30:00 -07:00
else :
2017-01-16 00:48:38 -08:00
self . load_2fa ( )
self . run ( ' on_restore_seed ' , seed , is_ext )
else :
2017-02-05 02:38:44 -08:00
raise BaseException ( ' Unknown seed type ' , self . seed_type )
2016-09-27 21:30:00 -07:00
def on_restore_bip39 ( self , seed , passphrase ) :
2017-09-15 03:20:06 -07:00
f = lambda x : self . run ( ' on_bip43 ' , seed , passphrase , str ( x ) )
2017-06-20 01:47:02 -07:00
self . derivation_dialog ( f )
2016-08-15 02:48:33 -07:00
2016-08-25 03:18:51 -07:00
def create_keystore ( self , seed , passphrase ) :
2017-10-25 08:33:49 -07:00
k = keystore . from_seed ( seed , passphrase , self . wallet_type == ' multisig ' )
2016-08-30 00:51:53 -07:00
self . on_keystore ( k )
2016-08-24 21:43:27 -07:00
2017-09-15 03:20:06 -07:00
def on_bip43 ( self , seed , passphrase , derivation ) :
2017-10-02 18:30:40 -07:00
k = keystore . from_bip39_seed ( seed , passphrase , derivation )
2016-08-25 03:18:51 -07:00
self . on_keystore ( k )
2016-07-01 23:58:56 -07:00
2016-08-25 03:18:51 -07:00
def on_keystore ( self , k ) :
2017-10-26 11:30:39 -07:00
has_xpub = isinstance ( k , keystore . Xpub )
if has_xpub :
from . bitcoin import xpub_type
t1 = xpub_type ( k . xpub )
2016-06-16 10:25:44 -07:00
if self . wallet_type == ' standard ' :
2017-10-26 11:30:39 -07:00
if has_xpub and t1 not in [ ' standard ' , ' p2wpkh ' , ' p2wpkh-p2sh ' ] :
2017-10-26 08:43:41 -07:00
self . show_error ( _ ( ' Wrong key type ' ) + ' %s ' % t1 )
self . run ( ' choose_keystore ' )
return
2016-08-25 03:18:51 -07:00
self . keystores . append ( k )
self . run ( ' create_wallet ' )
2016-06-20 07:25:11 -07:00
elif self . wallet_type == ' multisig ' :
2017-10-26 11:30:39 -07:00
assert has_xpub
2017-10-26 08:43:41 -07:00
if t1 not in [ ' standard ' , ' p2wsh ' , ' p2wsh-p2sh ' ] :
self . show_error ( _ ( ' Wrong key type ' ) + ' %s ' % t1 )
self . run ( ' choose_keystore ' )
return
2016-08-22 03:50:24 -07:00
if k . xpub in map ( lambda x : x . xpub , self . keystores ) :
2016-08-30 02:19:30 -07:00
self . show_error ( _ ( ' Error: duplicate master public key ' ) )
self . run ( ' choose_keystore ' )
return
2017-09-14 05:38:19 -07:00
if len ( self . keystores ) > 0 :
t2 = xpub_type ( self . keystores [ 0 ] . xpub )
if t1 != t2 :
self . show_error ( _ ( ' Cannot add this cosigner: ' ) + ' \n ' + " Their key type is ' %s ' , we are ' %s ' " % ( t1 , t2 ) )
self . run ( ' choose_keystore ' )
return
2016-08-22 03:50:24 -07:00
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 :
2016-08-25 03:18:51 -07:00
self . run ( ' create_wallet ' )
def create_wallet ( self ) :
2017-12-07 02:35:10 -08:00
encrypt_keystore = any ( k . may_have_password ( ) for k in self . keystores )
# note: the following condition ("if") is duplicated logic from
# wallet.get_available_storage_encryption_version()
if self . wallet_type == ' standard ' and isinstance ( self . keystores [ 0 ] , keystore . Hardware_KeyStore ) :
# offer encrypting with a pw derived from the hw device
k = self . keystores [ 0 ]
try :
k . handler = self . plugin . create_handler ( self )
password = k . get_password_for_storage_encryption ( )
except UserCancelled :
devmgr = self . plugins . device_manager
devmgr . unpair_xpub ( k . xpub )
self . choose_hw_device ( )
return
except BaseException as e :
traceback . print_exc ( file = sys . stderr )
self . show_error ( str ( e ) )
return
self . request_storage_encryption (
run_next = lambda encrypt_storage : self . on_password (
password ,
encrypt_storage = encrypt_storage ,
storage_enc_version = STO_EV_XPUB_PW ,
encrypt_keystore = False ) )
2016-08-25 03:18:51 -07:00
else :
2017-12-07 02:35:10 -08:00
# prompt the user to set an arbitrary password
self . request_password (
run_next = lambda password , encrypt_storage : self . on_password (
password ,
encrypt_storage = encrypt_storage ,
storage_enc_version = STO_EV_USER_PW ,
encrypt_keystore = encrypt_keystore ) ,
force_disable_encrypt_cb = not encrypt_keystore )
def on_password ( self , password , * , encrypt_storage ,
storage_enc_version = STO_EV_USER_PW , encrypt_keystore ) :
self . storage . set_keystore_encryption ( bool ( password ) and encrypt_keystore )
if encrypt_storage :
self . storage . set_password ( password , enc_version = storage_enc_version )
2016-08-25 03:18:51 -07:00
for k in self . keystores :
if k . may_have_password ( ) :
k . update_password ( None , password )
if self . wallet_type == ' standard ' :
2017-01-16 00:48:38 -08:00
self . storage . put ( ' seed_type ' , self . seed_type )
2017-02-05 02:38:44 -08:00
keys = self . keystores [ 0 ] . dump ( )
self . storage . put ( ' keystore ' , keys )
2017-01-25 12:41:26 -08:00
self . wallet = Standard_Wallet ( self . storage )
2016-08-25 03:18:51 -07:00
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 ' )
2017-12-07 02:35:10 -08:00
elif self . wallet_type == ' imported ' :
if len ( self . keystores ) > 0 :
keys = self . keystores [ 0 ] . dump ( )
self . storage . put ( ' keystore ' , keys )
self . wallet = Imported_Wallet ( self . storage )
self . wallet . storage . write ( )
self . terminate ( )
2016-08-22 03:50:24 -07:00
def show_xpub_and_add_cosigners ( self , xpub ) :
self . show_xpub_dialog ( xpub = xpub , run_next = lambda x : self . run ( ' choose_keystore ' ) )
2016-07-01 02:44:26 -07:00
2016-07-01 23:58:56 -07:00
def on_cosigner ( self , text , password , i ) :
2017-09-25 12:35:14 -07:00
k = keystore . from_master_key ( text , password )
2016-08-22 03:50:24 -07:00
self . on_keystore ( k )
2016-06-16 10:25:44 -07:00
2017-09-14 03:20:11 -07:00
def choose_seed_type ( self ) :
title = _ ( ' Choose Seed type ' )
message = ' ' . join ( [
2018-01-05 04:42:06 -08:00
_ ( " The type of addresses used by your wallet will depend on your seed. " ) ,
_ ( " Segwit wallets use bech32 addresses, defined in BIP173. " ) ,
_ ( " Please note that websites and other wallets may not support these addresses yet. " ) ,
_ ( " Thus, you might want to keep using a non-segwit wallet in order to be able to receive bitcoins during the transition period. " )
2017-09-14 03:20:11 -07:00
] )
choices = [
( ' create_standard_seed ' , _ ( ' Standard ' ) ) ,
( ' create_segwit_seed ' , _ ( ' Segwit ' ) ) ,
]
self . choice_dialog ( title = title , message = message , choices = choices , run_next = self . run )
def create_segwit_seed ( self ) : self . create_seed ( ' segwit ' )
def create_standard_seed ( self ) : self . create_seed ( ' standard ' )
def create_seed ( self , seed_type ) :
2017-02-05 02:38:44 -08:00
from . import mnemonic
2017-09-14 03:20:11 -07:00
self . seed_type = seed_type
2017-01-16 00:48:38 -08:00
seed = mnemonic . Mnemonic ( ' en ' ) . make_seed ( self . seed_type )
2016-08-28 01:33:01 -07:00
self . opt_bip39 = False
2016-09-29 16:15:28 -07:00
f = lambda x : self . request_passphrase ( seed , x )
self . show_seed_dialog ( run_next = f , seed_text = seed )
2016-08-30 00:51:53 -07:00
2016-09-29 16:15:28 -07:00
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 , ' ' )
2016-06-16 10:25:44 -07:00
2016-08-29 06:33:16 -07:00
def confirm_seed ( self , seed , passphrase ) :
2016-08-30 00:51:53 -07:00
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 ) :
2016-08-30 01:36:51 -07:00
f = lambda x : self . run ( ' create_keystore ' , seed , x )
2016-08-30 00:51:53 -07:00
if passphrase :
2016-10-10 08:11:46 -07:00
title = _ ( ' Confirm Seed Extension ' )
2016-08-30 00:51:53 -07:00
message = ' \n ' . join ( [
2016-10-10 08:11:46 -07:00
_ ( ' Your seed extension must be saved together with your seed. ' ) ,
2016-08-30 00:51:53 -07:00
_ ( ' Please type it here. ' ) ,
] )
self . line_dialog ( run_next = f , title = title , message = message , default = ' ' , test = lambda x : x == passphrase )
else :
2016-08-30 01:36:51 -07:00
f ( ' ' )
2016-07-01 23:58:56 -07:00
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 )