@ -24,12 +24,19 @@
# SOFTWARE.
# SOFTWARE.
import os
import os
import sys
import traceback
from . import bitcoin
from . import bitcoin
from . import keystore
from . import keystore
from . keystore import bip44_derivation
from . keystore import bip44_derivation
from . wallet import Imported_Wallet , Standard_Wallet , Multisig_Wallet , wallet_types
from . wallet import Imported_Wallet , Standard_Wallet , Multisig_Wallet , wallet_types
from . storage import STO_EV_USER_PW , STO_EV_XPUB_PW , get_derivation_used_for_hw_device_encryption
from . i18n import _
from . i18n import _
from . util import UserCancelled
# hardware device setup purpose
HWD_SETUP_NEW_WALLET , HWD_SETUP_DECRYPT_WALLET = range ( 0 , 2 )
class ScriptTypeNotSupported ( Exception ) : pass
class ScriptTypeNotSupported ( Exception ) : pass
@ -147,17 +154,22 @@ class BaseWizard(object):
is_valid = v , allow_multi = True )
is_valid = v , allow_multi = True )
def on_import ( self , text ) :
def on_import ( self , text ) :
# create a temporary wallet and exploit that modifications
# will be reflected on self.storage
if keystore . is_address_list ( text ) :
if keystore . is_address_list ( text ) :
self . wallet = Imported_Wallet ( self . storage )
w = Imported_Wallet ( self . storage )
for x in text . split ( ) :
for x in text . split ( ) :
self . wallet . import_address ( x )
w . import_address ( x )
elif keystore . is_private_key_list ( text ) :
elif keystore . is_private_key_list ( text ) :
k = keystore . Imported_KeyStore ( { } )
k = keystore . Imported_KeyStore ( { } )
self . storage . put ( ' keystore ' , k . dump ( ) )
self . storage . put ( ' keystore ' , k . dump ( ) )
self . wallet = Imported_Wallet ( self . storage )
w = Imported_Wallet ( self . storage )
for x in text . split ( ) :
for x in text . split ( ) :
self . wallet . import_private_key ( x , None )
w . import_private_key ( x , None )
self . terminate ( )
self . keystores . append ( w . keystore )
else :
return self . terminate ( )
return self . run ( ' create_wallet ' )
def restore_from_key ( self ) :
def restore_from_key ( self ) :
if self . wallet_type == ' standard ' :
if self . wallet_type == ' standard ' :
@ -176,7 +188,7 @@ class BaseWizard(object):
k = keystore . from_master_key ( text )
k = keystore . from_master_key ( text )
self . on_keystore ( k )
self . on_keystore ( k )
def choose_hw_device ( self ) :
def choose_hw_device ( self , purpose = HWD_SETUP_NEW_WALLET ) :
title = _ ( ' Hardware Keystore ' )
title = _ ( ' Hardware Keystore ' )
# check available plugins
# check available plugins
support = self . plugins . get_hardware_support ( )
support = self . plugins . get_hardware_support ( )
@ -185,7 +197,7 @@ class BaseWizard(object):
_ ( ' No hardware wallet support found on your system. ' ) ,
_ ( ' No hardware wallet support found on your system. ' ) ,
_ ( ' Please install the relevant libraries (eg python-trezor for Trezor). ' ) ,
_ ( ' 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 ( ) )
self . confirm_dialog ( title = title , message = msg , run_next = lambda x : self . choose_hw_device ( purpose ) )
return
return
# scan devices
# scan devices
devices = [ ]
devices = [ ]
@ -205,7 +217,7 @@ class BaseWizard(object):
_ ( ' If your device is not detected on Windows, go to " Settings " , " Devices " , " Connected devices " , and do " Remove device " . Then, plug your device again. ' ) + ' ' ,
_ ( ' 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. ' ) ,
_ ( ' On Linux, you might have to add a new permission to your udev rules. ' ) ,
] )
] )
self . confirm_dialog ( title = title , message = msg , run_next = lambda x : self . choose_hw_device ( ) )
self . confirm_dialog ( title = title , message = msg , run_next = lambda x : self . choose_hw_device ( purpose ) )
return
return
# select device
# select device
self . devices = devices
self . devices = devices
@ -216,16 +228,17 @@ class BaseWizard(object):
descr = " %s [ %s , %s ] " % ( label , name , state )
descr = " %s [ %s , %s ] " % ( label , name , state )
choices . append ( ( ( name , info ) , descr ) )
choices . append ( ( ( name , info ) , descr ) )
msg = _ ( ' Select a device ' ) + ' : '
msg = _ ( ' Select a device ' ) + ' : '
self . choice_dialog ( title = title , message = msg , choices = choices , run_next = self . on_device )
self . choice_dialog ( title = title , message = msg , choices = choices , run_next = lambda * args : self . on_device ( * args , purpose = purpose ) )
def on_device ( self , name , device_info ) :
def on_device ( self , name , device_info , * , purpose ) :
self . plugin = self . plugins . get_plugin ( name )
self . plugin = self . plugins . get_plugin ( name )
try :
try :
self . plugin . setup_device ( device_info , self )
self . plugin . setup_device ( device_info , self , purpose )
except BaseException as e :
except BaseException as e :
self . show_error ( str ( e ) )
self . show_error ( str ( e ) )
self . choose_hw_device ( )
self . choose_hw_device ( purpose )
return
return
if purpose == HWD_SETUP_NEW_WALLET :
if self . wallet_type == ' multisig ' :
if self . wallet_type == ' multisig ' :
# There is no general standard for HD multisig.
# There is no general standard for HD multisig.
# This is partially compatible with BIP45; assumes index=0
# This is partially compatible with BIP45; assumes index=0
@ -233,6 +246,13 @@ class BaseWizard(object):
else :
else :
f = lambda x : self . run ( ' on_hw_derivation ' , name , device_info , str ( x ) )
f = lambda x : self . run ( ' on_hw_derivation ' , name , device_info , str ( x ) )
self . derivation_dialog ( f )
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 )
else :
raise Exception ( ' unknown purpose: %s ' % purpose )
def derivation_dialog ( self , f ) :
def derivation_dialog ( self , f ) :
default = bip44_derivation ( 0 , bip43_purpose = 44 )
default = bip44_derivation ( 0 , bip43_purpose = 44 )
@ -365,13 +385,45 @@ class BaseWizard(object):
self . run ( ' create_wallet ' )
self . run ( ' create_wallet ' )
def create_wallet ( self ) :
def create_wallet ( self ) :
if any ( k . may_have_password ( ) for k in self . keystores ) :
encrypt_keystore = any ( k . may_have_password ( ) for k in self . keystores )
self . request_password ( run_next = self . on_password )
# 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 ) )
else :
else :
self . on_password ( None , False )
# prompt the user to set an arbitrary password
self . request_password (
def on_password ( self , password , encrypt ) :
run_next = lambda password , encrypt_storage : self . on_password (
self . storage . set_password ( password , encrypt )
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 )
for k in self . keystores :
for k in self . keystores :
if k . may_have_password ( ) :
if k . may_have_password ( ) :
k . update_password ( None , password )
k . update_password ( None , password )
@ -387,6 +439,13 @@ class BaseWizard(object):
self . storage . write ( )
self . storage . write ( )
self . wallet = Multisig_Wallet ( self . storage )
self . wallet = Multisig_Wallet ( self . storage )
self . run ( ' create_addresses ' )
self . run ( ' create_addresses ' )
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 ( )
def show_xpub_and_add_cosigners ( self , xpub ) :
def show_xpub_and_add_cosigners ( self , xpub ) :
self . show_xpub_dialog ( xpub = xpub , run_next = lambda x : self . run ( ' choose_keystore ' ) )
self . show_xpub_dialog ( xpub = xpub , run_next = lambda x : self . run ( ' choose_keystore ' ) )