2015-12-13 18:13:24 +09:00
#!/usr/bin/env python
#
# Electrum - Lightweight Bitcoin Client
# Copyright (C) 2015 Thomas Voegtlin
#
2016-02-23 11:36:42 +01:00
# 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:
2015-12-13 18:13:24 +09:00
#
2016-02-23 11:36:42 +01:00
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
2015-12-13 18:13:24 +09:00
#
2016-02-23 11:36:42 +01:00
# 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.
2015-12-13 18:13:24 +09:00
2015-11-28 00:22:06 +01:00
from functools import partial
2019-02-11 20:21:24 +01:00
import os
2020-12-08 10:33:43 +01:00
from typing import TYPE_CHECKING
2015-11-28 00:22:06 +01:00
2024-09-05 16:20:01 +00:00
from PyQt6 . QtGui import QPixmap , QMovie , QColor
from PyQt6 . QtCore import QObject , pyqtSignal , QSize , Qt
from PyQt6 . QtWidgets import ( QTextEdit , QVBoxLayout , QLabel , QGridLayout , QHBoxLayout ,
2024-10-21 14:11:18 +02:00
QRadioButton , QCheckBox , QPushButton , QWidget )
2015-11-26 14:15:54 +01:00
2023-08-03 20:43:16 +02:00
from electrum . i18n import _
from electrum . plugin import hook
2024-10-21 14:11:18 +02:00
from electrum . util import InvalidPassword
2023-08-03 20:43:16 +02:00
from electrum . logging import Logger , get_logger
2023-08-28 16:22:37 +02:00
from electrum import keystore
2023-08-03 20:43:16 +02:00
2024-10-21 14:11:18 +02:00
from electrum . gui . qt . util import ( WindowModalDialog , WaitingDialog , OkButton , CancelButton , Buttons , icon_path ,
internal_plugin_icon_path , WWLabel , CloseButton , ColorScheme ,
2024-10-08 00:04:20 +02:00
ChoiceWidget , PasswordLineEdit , char_width_in_lineedit )
2018-07-11 17:38:47 +02:00
from electrum . gui . qt . qrcodewidget import QRCodeWidget
from electrum . gui . qt . amountedit import AmountEdit
from electrum . gui . qt . main_window import StatusBarButton
2024-10-21 16:14:12 +02:00
from electrum . gui . qt . wizard . wallet import ( WCCreateSeed , WCConfirmSeed , WCHaveSeed , WCEnterExt , WCConfirmExt ,
WalletWizardComponent )
2024-10-17 13:01:41 +02:00
from electrum . gui . qt . util import read_QIcon_from_bytes
2023-08-03 20:43:16 +02:00
2023-08-31 08:54:53 +02:00
from . common_qt import TrustedcoinPluginQObject
2024-10-21 14:11:18 +02:00
from . trustedcoin import TrustedCoinPlugin , DISCLAIMER
2019-04-26 18:52:26 +02:00
2020-12-08 10:33:43 +01:00
if TYPE_CHECKING :
from electrum . gui . qt . main_window import ElectrumWindow
from electrum . wallet import Abstract_Wallet
2023-08-11 15:55:54 +02:00
from electrum . gui . qt . wizard . wallet import QENewWalletWizard
2020-12-08 10:33:43 +01:00
2015-11-23 19:38:48 +01:00
2017-10-18 16:11:30 +02:00
class TOS ( QTextEdit ) :
tos_signal = pyqtSignal ( )
2017-12-04 12:02:06 +01:00
error_signal = pyqtSignal ( object )
2017-09-23 05:54:38 +02:00
2019-04-26 18:52:26 +02:00
class HandlerTwoFactor ( QObject , Logger ) :
2018-05-08 20:04:36 +02:00
def __init__ ( self , plugin , window ) :
2019-04-26 18:52:26 +02:00
QObject . __init__ ( self )
2018-05-08 20:04:36 +02:00
self . plugin = plugin
self . window = window
2019-04-26 18:52:26 +02:00
Logger . __init__ ( self )
2018-05-08 20:04:36 +02:00
2018-05-18 18:07:52 +02:00
def prompt_user_for_otp ( self , wallet , tx , on_success , on_failure ) :
if not isinstance ( wallet , self . plugin . wallet_class ) :
return
if wallet . can_sign_without_server ( ) :
return
2020-02-04 12:45:31 +01:00
if not wallet . keystores [ ' x3 ' ] . can_sign ( tx , ignore_watching_only = True ) :
2019-04-26 18:52:26 +02:00
self . logger . info ( " twofactor: xpub3 not needed " )
2018-05-18 18:07:52 +02:00
return
window = self . window . top_level_window ( )
auth_code = self . plugin . auth_dialog ( window )
2019-03-25 23:36:52 +01:00
WaitingDialog ( parent = window ,
message = _ ( ' Waiting for TrustedCoin server to sign transaction... ' ) ,
task = lambda : wallet . on_otp ( tx , auth_code ) ,
on_success = lambda * args : on_success ( tx ) ,
on_error = on_failure )
2018-05-08 20:04:36 +02:00
2015-11-23 19:38:48 +01:00
class Plugin ( TrustedCoinPlugin ) :
2017-09-23 05:54:38 +02:00
def __init__ ( self , parent , config , name ) :
super ( ) . __init__ ( parent , config , name )
2015-11-26 14:15:54 +01:00
@hook
2020-12-08 10:33:43 +01:00
def load_wallet ( self , wallet : ' Abstract_Wallet ' , window : ' ElectrumWindow ' ) :
2016-01-03 19:16:37 +01:00
if not isinstance ( wallet , self . wallet_class ) :
return
2018-05-08 20:04:36 +02:00
wallet . handler_2fa = HandlerTwoFactor ( self , window )
2016-01-03 19:16:37 +01:00
if wallet . can_sign_without_server ( ) :
msg = ' ' . join ( [
2017-09-24 01:42:24 +02:00
_ ( ' This wallet was restored from seed, and it contains two master private keys. ' ) ,
2016-01-03 19:16:37 +01:00
_ ( ' Therefore, two-factor authentication is disabled. ' )
] )
action = lambda : window . show_message ( msg )
2024-10-17 13:01:41 +02:00
icon = read_QIcon_from_bytes ( self . read_file ( " trustedcoin-status-disabled.png " ) )
2016-01-03 19:16:37 +01:00
else :
action = partial ( self . settings_dialog , window )
2024-10-17 13:01:41 +02:00
icon = read_QIcon_from_bytes ( self . read_file ( " trustedcoin-status.png " ) )
2023-03-14 17:28:33 +01:00
sb = window . statusBar ( )
button = StatusBarButton ( icon , _ ( " TrustedCoin " ) , action , sb . height ( ) )
sb . addPermanentWidget ( button )
2017-07-06 16:03:21 +02:00
self . start_request_thread ( window . wallet )
2015-11-26 14:15:54 +01:00
2015-11-23 19:38:48 +01:00
def auth_dialog ( self , window ) :
2015-12-23 12:20:19 +09:00
d = WindowModalDialog ( window , _ ( " Authorization " ) )
2015-11-23 19:38:48 +01:00
vbox = QVBoxLayout ( d )
2024-10-21 14:11:18 +02:00
pw = AmountEdit ( None , is_int = True )
2015-11-23 19:38:48 +01:00
msg = _ ( ' Please enter your Google Authenticator code ' )
vbox . addWidget ( QLabel ( msg ) )
grid = QGridLayout ( )
grid . setSpacing ( 8 )
grid . addWidget ( QLabel ( _ ( ' Code ' ) ) , 1 , 0 )
grid . addWidget ( pw , 1 , 1 )
vbox . addLayout ( grid )
2017-11-13 11:47:25 +01:00
msg = _ ( ' If you have lost your second factor, you need to restore your wallet from seed in order to request a new code. ' )
label = QLabel ( msg )
label . setWordWrap ( 1 )
vbox . addWidget ( label )
2015-11-23 19:38:48 +01:00
vbox . addLayout ( Buttons ( CancelButton ( d ) , OkButton ( d ) ) )
2024-09-05 16:20:01 +00:00
if not d . exec ( ) :
2015-11-23 19:38:48 +01:00
return
return pw . get_amount ( )
2018-05-18 18:07:52 +02:00
def prompt_user_for_otp ( self , wallet , tx , on_success , on_failure ) :
wallet . handler_2fa . prompt_user_for_otp ( wallet , tx , on_success , on_failure )
2015-11-23 19:38:48 +01:00
2019-03-16 20:05:10 +01:00
def waiting_dialog_for_billing_info ( self , window , * , on_finished = None ) :
def task ( ) :
return self . request_billing_info ( window . wallet , suppress_connection_error = False )
2024-10-21 14:11:18 +02:00
2019-03-16 20:05:10 +01:00
def on_error ( exc_info ) :
e = exc_info [ 1 ]
window . show_error ( " {header} \n {exc} \n \n {tor} "
. format ( header = _ ( ' Error getting TrustedCoin account info. ' ) ,
2019-07-17 20:12:52 +02:00
exc = repr ( e ) ,
2019-03-16 20:05:10 +01:00
tor = _ ( ' If you keep experiencing network problems, try using a Tor proxy. ' ) ) )
return WaitingDialog ( parent = window ,
message = _ ( ' Requesting account info from TrustedCoin server... ' ) ,
task = task ,
on_success = on_finished ,
on_error = on_error )
2015-12-23 22:21:29 +09:00
2015-11-23 19:38:48 +01:00
@hook
def abort_send ( self , window ) :
wallet = window . wallet
2016-01-03 19:16:37 +01:00
if not isinstance ( wallet , self . wallet_class ) :
return
2017-07-06 16:03:21 +02:00
if wallet . can_sign_without_server ( ) :
return
if wallet . billing_info is None :
2019-03-16 20:05:10 +01:00
self . waiting_dialog_for_billing_info ( window )
2017-07-06 16:03:21 +02:00
return True
2015-11-23 19:38:48 +01:00
return False
def settings_dialog ( self , window ) :
2019-03-16 20:05:10 +01:00
self . waiting_dialog_for_billing_info ( window ,
on_finished = partial ( self . show_settings_dialog , window ) )
2015-11-23 19:38:48 +01:00
2024-10-17 13:01:41 +02:00
def icon_path ( self , name ) :
return internal_plugin_icon_path ( self . name , name )
2015-11-23 19:38:48 +01:00
def show_settings_dialog ( self , window , success ) :
if not success :
window . show_message ( _ ( ' Server not reachable. ' ) )
return
wallet = window . wallet
2015-12-23 12:20:19 +09:00
d = WindowModalDialog ( window , _ ( " TrustedCoin Information " ) )
2015-11-23 19:38:48 +01:00
d . setMinimumSize ( 500 , 200 )
vbox = QVBoxLayout ( d )
hbox = QHBoxLayout ( )
logo = QLabel ( )
2024-10-17 13:01:41 +02:00
logo . setPixmap ( QPixmap ( self . icon_path ( " trustedcoin-status.png " ) ) )
2015-11-23 19:38:48 +01:00
msg = _ ( ' This wallet is protected by TrustedCoin \' s two-factor authentication. ' ) + ' <br/> ' \
+ _ ( " For more information, visit " ) + " <a href= \" https://api.trustedcoin.com/#/electrum-help \" >https://api.trustedcoin.com/#/electrum-help</a> "
label = QLabel ( msg )
label . setOpenExternalLinks ( 1 )
hbox . addStretch ( 10 )
hbox . addWidget ( logo )
hbox . addStretch ( 10 )
hbox . addWidget ( label )
hbox . addStretch ( 10 )
vbox . addLayout ( hbox )
vbox . addStretch ( 10 )
2018-04-15 20:45:30 +03:00
msg = _ ( ' TrustedCoin charges a small fee to co-sign transactions. The fee depends on how many prepaid transactions you buy. An extra output is added to your transaction every time you run out of prepaid transactions. ' ) + ' <br/> '
2015-11-23 19:38:48 +01:00
label = QLabel ( msg )
label . setWordWrap ( 1 )
vbox . addWidget ( label )
vbox . addStretch ( 10 )
grid = QGridLayout ( )
vbox . addLayout ( grid )
price_per_tx = wallet . price_per_tx
2019-09-10 16:24:21 +02:00
n_prepay = wallet . num_prepay ( )
2017-05-12 15:58:00 +02:00
i = 0
2015-11-23 19:38:48 +01:00
for k , v in sorted ( price_per_tx . items ( ) ) :
if k == 1 :
continue
2017-05-12 15:58:00 +02:00
grid . addWidget ( QLabel ( " Pay every %d transactions: " % k ) , i , 0 )
grid . addWidget ( QLabel ( window . format_amount ( v / k ) + ' ' + window . base_unit ( ) + " /tx " ) , i , 1 )
b = QRadioButton ( )
b . setChecked ( k == n_prepay )
2024-10-21 14:11:18 +02:00
2023-05-24 17:41:44 +00:00
def on_click ( b , k ) :
self . config . PLUGIN_TRUSTEDCOIN_NUM_PREPAY = k
b . clicked . connect ( partial ( on_click , k = k ) )
2015-11-23 19:38:48 +01:00
grid . addWidget ( b , i , 2 )
i + = 1
n = wallet . billing_info . get ( ' tx_remaining ' , 0 )
2018-02-04 07:26:55 +01:00
grid . addWidget ( QLabel ( _ ( " Your wallet has {} prepaid transactions. " ) . format ( n ) ) , i , 0 )
2015-11-23 19:38:48 +01:00
vbox . addLayout ( Buttons ( CloseButton ( d ) ) )
2024-09-05 16:20:01 +00:00
d . exec ( )
2015-11-23 19:38:48 +01:00
2023-08-01 18:02:46 +02:00
@hook
2023-08-11 15:55:54 +02:00
def init_wallet_wizard ( self , wizard : ' QENewWalletWizard ' ) :
2023-08-24 15:31:47 +02:00
wizard . trustedcoin_qhelper = TrustedcoinPluginQObject ( self , wizard , None )
2023-08-01 18:02:46 +02:00
self . extend_wizard ( wizard )
2024-05-16 00:08:07 +02:00
if wizard . start_viewstate and wizard . start_viewstate . view . startswith ( ' trustedcoin_ ' ) :
2024-10-17 13:01:41 +02:00
wizard . start_viewstate . params . update ( { ' icon ' : self . icon_path ( ' trustedcoin-wizard.png ' ) } )
2023-08-01 18:02:46 +02:00
2023-08-11 15:55:54 +02:00
def extend_wizard ( self , wizard : ' QENewWalletWizard ' ) :
2023-08-03 20:43:16 +02:00
super ( ) . extend_wizard ( wizard )
2023-08-01 18:02:46 +02:00
views = {
' trustedcoin_start ' : {
' gui ' : WCDisclaimer ,
2024-10-17 13:01:41 +02:00
' params ' : { ' icon ' : self . icon_path ( ' trustedcoin-wizard.png ' ) } ,
2023-08-01 18:02:46 +02:00
} ,
' trustedcoin_choose_seed ' : {
' gui ' : WCChooseSeed ,
2024-10-17 13:01:41 +02:00
' params ' : { ' icon ' : self . icon_path ( ' trustedcoin-wizard.png ' ) } ,
2023-08-01 18:02:46 +02:00
} ,
' trustedcoin_create_seed ' : {
' gui ' : WCCreateSeed ,
2024-10-17 13:01:41 +02:00
' params ' : { ' icon ' : self . icon_path ( ' trustedcoin-wizard.png ' ) } ,
2023-08-01 18:02:46 +02:00
} ,
' trustedcoin_confirm_seed ' : {
' gui ' : WCConfirmSeed ,
2024-10-17 13:01:41 +02:00
' params ' : { ' icon ' : self . icon_path ( ' trustedcoin-wizard.png ' ) } ,
2023-08-01 18:02:46 +02:00
} ,
' trustedcoin_have_seed ' : {
' gui ' : WCHaveSeed ,
2024-10-17 13:01:41 +02:00
' params ' : { ' icon ' : self . icon_path ( ' trustedcoin-wizard.png ' ) } ,
2023-08-01 18:02:46 +02:00
} ,
2023-08-03 20:43:16 +02:00
' trustedcoin_keep_disable ' : {
' gui ' : WCKeepDisable ,
2024-10-17 13:01:41 +02:00
' params ' : { ' icon ' : self . icon_path ( ' trustedcoin-wizard.png ' ) } ,
2023-08-03 20:43:16 +02:00
} ,
2023-12-08 11:56:11 +01:00
' trustedcoin_tos ' : {
2023-08-03 20:43:16 +02:00
' gui ' : WCTerms ,
2024-10-17 13:01:41 +02:00
' params ' : { ' icon ' : self . icon_path ( ' trustedcoin-wizard.png ' ) } ,
2023-08-03 20:43:16 +02:00
} ,
2024-10-08 00:04:20 +02:00
' trustedcoin_keystore_unlock ' : {
' gui ' : WCKeystorePassword ,
2024-10-17 13:01:41 +02:00
' params ' : { ' icon ' : self . icon_path ( ' trustedcoin-wizard.png ' ) } ,
2024-10-08 00:04:20 +02:00
} ,
2023-08-03 20:43:16 +02:00
' trustedcoin_show_confirm_otp ' : {
' gui ' : WCShowConfirmOTP ,
2024-10-17 13:01:41 +02:00
' params ' : { ' icon ' : self . icon_path ( ' trustedcoin-wizard.png ' ) } ,
2023-08-03 20:43:16 +02:00
}
2023-08-01 18:02:46 +02:00
}
wizard . navmap_merge ( views )
# modify default flow, insert seed extension entry/confirm as separate views
ext = {
' trustedcoin_create_seed ' : {
' next ' : lambda d : ' trustedcoin_create_ext ' if wizard . wants_ext ( d ) else ' trustedcoin_confirm_seed '
} ,
' trustedcoin_create_ext ' : {
' gui ' : WCEnterExt ,
2024-10-17 13:01:41 +02:00
' params ' : { ' icon ' : self . icon_path ( ' trustedcoin-wizard.png ' ) } ,
2023-08-01 18:02:46 +02:00
' next ' : ' trustedcoin_confirm_seed ' ,
} ,
' trustedcoin_confirm_seed ' : {
2023-12-08 11:56:11 +01:00
' next ' : lambda d : ' trustedcoin_confirm_ext ' if wizard . wants_ext ( d ) else ' trustedcoin_tos '
2023-08-01 18:02:46 +02:00
} ,
' trustedcoin_confirm_ext ' : {
' gui ' : WCConfirmExt ,
2024-10-17 13:01:41 +02:00
' params ' : { ' icon ' : self . icon_path ( ' trustedcoin-wizard.png ' ) } ,
2023-12-08 11:56:11 +01:00
' next ' : ' trustedcoin_tos ' ,
2023-08-01 18:02:46 +02:00
} ,
' trustedcoin_have_seed ' : {
' next ' : lambda d : ' trustedcoin_have_ext ' if wizard . wants_ext ( d ) else ' trustedcoin_keep_disable '
} ,
' trustedcoin_have_ext ' : {
' gui ' : WCEnterExt ,
2024-10-17 13:01:41 +02:00
' params ' : { ' icon ' : self . icon_path ( ' trustedcoin-wizard.png ' ) } ,
2023-08-01 18:02:46 +02:00
' next ' : ' trustedcoin_keep_disable ' ,
} ,
}
wizard . navmap_merge ( ext )
2023-08-28 16:22:37 +02:00
# insert page offering choice to go online or continue on another system
ext_online = {
' trustedcoin_continue_online ' : {
' gui ' : WCContinueOnline ,
2024-10-17 13:01:41 +02:00
' params ' : { ' icon ' : self . icon_path ( ' trustedcoin-wizard.png ' ) } ,
2023-12-08 11:56:11 +01:00
' next ' : lambda d : ' trustedcoin_tos ' if d [ ' trustedcoin_go_online ' ] else ' wallet_password ' ,
2023-08-28 16:22:37 +02:00
' accept ' : self . on_continue_online ,
' last ' : lambda d : not d [ ' trustedcoin_go_online ' ] and wizard . is_single_password ( )
} ,
' trustedcoin_confirm_seed ' : {
' next ' : lambda d : ' trustedcoin_confirm_ext ' if wizard . wants_ext ( d ) else ' trustedcoin_continue_online '
} ,
' trustedcoin_confirm_ext ' : {
' next ' : ' trustedcoin_continue_online ' ,
} ,
' trustedcoin_keep_disable ' : {
' next ' : lambda d : ' trustedcoin_continue_online ' if d [ ' trustedcoin_keepordisable ' ] != ' disable '
else ' wallet_password ' ,
}
}
wizard . navmap_merge ( ext_online )
def on_continue_online ( self , wizard_data ) :
if not wizard_data [ ' trustedcoin_go_online ' ] :
self . logger . debug ( ' Staying offline, create keystores here ' )
xprv1 , xpub1 , xprv2 , xpub2 , xpub3 , short_id = self . create_keys ( wizard_data )
k1 = keystore . from_xprv ( xprv1 )
k2 = keystore . from_xpub ( xpub2 )
2020-02-04 12:45:31 +01:00
wizard_data [ ' x1 ' ] = k1 . dump ( )
wizard_data [ ' x2 ' ] = k2 . dump ( )
2023-08-28 16:22:37 +02:00
2023-08-01 18:02:46 +02:00
2024-10-21 16:14:12 +02:00
class WCDisclaimer ( WalletWizardComponent ) :
2023-08-01 18:02:46 +02:00
def __init__ ( self , parent , wizard ) :
2024-10-21 16:14:12 +02:00
WalletWizardComponent . __init__ ( self , parent , wizard , title = _ ( ' Disclaimer ' ) )
2023-08-01 18:02:46 +02:00
self . layout ( ) . addWidget ( WWLabel ( ' \n \n ' . join ( DISCLAIMER ) ) )
self . layout ( ) . addStretch ( 1 )
self . _valid = True
def apply ( self ) :
pass
2024-10-21 16:14:12 +02:00
class WCChooseSeed ( WalletWizardComponent ) :
2023-08-01 18:02:46 +02:00
def __init__ ( self , parent , wizard ) :
2024-10-21 16:14:12 +02:00
WalletWizardComponent . __init__ ( self , parent , wizard , title = _ ( ' Create or restore ' ) )
2023-08-01 18:02:46 +02:00
message = _ ( ' Do you want to create a new seed, or restore a wallet using an existing seed? ' )
choices = [
( ' createseed ' , _ ( ' Create a new seed ' ) ) ,
( ' haveseed ' , _ ( ' I already have a seed ' ) ) ,
]
2023-08-14 16:22:19 +02:00
self . choice_w = ChoiceWidget ( message = message , choices = choices )
self . layout ( ) . addWidget ( self . choice_w )
2023-08-01 18:02:46 +02:00
self . layout ( ) . addStretch ( 1 )
self . _valid = True
def apply ( self ) :
2024-02-05 14:39:03 +01:00
self . wizard_data [ ' keystore_type ' ] = self . choice_w . selected_key
2023-08-03 20:43:16 +02:00
2024-10-21 16:14:12 +02:00
class WCTerms ( WalletWizardComponent ) :
2023-08-03 20:43:16 +02:00
def __init__ ( self , parent , wizard ) :
2024-10-21 16:14:12 +02:00
WalletWizardComponent . __init__ ( self , parent , wizard , title = _ ( ' Terms and conditions ' ) )
2023-08-03 20:43:16 +02:00
self . _has_tos = False
self . tos_e = TOS ( )
self . tos_e . setReadOnly ( True )
self . layout ( ) . addWidget ( self . tos_e )
2024-10-21 14:11:18 +02:00
def on_ready ( self ) :
2023-08-03 20:43:16 +02:00
self . fetch_terms_and_conditions ( )
def fetch_terms_and_conditions ( self ) :
2023-08-24 15:31:47 +02:00
self . wizard . trustedcoin_qhelper . busyChanged . connect ( self . on_busy_changed )
self . wizard . trustedcoin_qhelper . termsAndConditionsRetrieved . connect ( self . on_terms_retrieved )
self . wizard . trustedcoin_qhelper . termsAndConditionsError . connect ( self . on_terms_error )
self . wizard . trustedcoin_qhelper . fetchTermsAndConditions ( )
2023-08-03 20:43:16 +02:00
def on_busy_changed ( self ) :
2023-08-24 15:31:47 +02:00
self . busy = self . wizard . trustedcoin_qhelper . busy
2023-08-03 20:43:16 +02:00
def on_terms_retrieved ( self , tos : str ) - > None :
self . _has_tos = True
self . tos_e . setText ( tos )
self . validate ( )
def on_terms_error ( self , error : str ) - > None :
self . error = error
def validate ( self ) :
2023-12-08 11:56:11 +01:00
self . valid = self . _has_tos
2023-08-03 20:43:16 +02:00
def apply ( self ) :
2023-12-08 11:56:11 +01:00
pass
2023-08-03 20:43:16 +02:00
2024-10-21 16:14:12 +02:00
class WCShowConfirmOTP ( WalletWizardComponent ) :
2023-08-03 20:43:16 +02:00
_logger = get_logger ( __name__ )
def __init__ ( self , parent , wizard ) :
2024-10-21 16:14:12 +02:00
WalletWizardComponent . __init__ ( self , parent , wizard , title = _ ( ' Authenticator secret ' ) )
2023-08-04 13:03:21 +02:00
self . _otp_verified = False
2024-01-15 17:45:29 +01:00
self . _is_online_continuation = False
2023-08-03 20:43:16 +02:00
self . new_otp = QWidget ( )
new_otp_layout = QVBoxLayout ( )
scanlabel = WWLabel ( _ ( ' Enter or scan into authenticator app. Then authenticate below ' ) )
new_otp_layout . addWidget ( scanlabel )
self . qr = QRCodeWidget ( ' ' )
new_otp_layout . addWidget ( self . qr )
self . secretlabel = WWLabel ( )
new_otp_layout . addWidget ( self . secretlabel )
self . new_otp . setLayout ( new_otp_layout )
self . exist_otp = QWidget ( )
exist_otp_layout = QVBoxLayout ( )
knownlabel = WWLabel ( _ ( ' This wallet is already registered with TrustedCoin. ' ) )
exist_otp_layout . addWidget ( knownlabel )
2024-01-15 17:45:29 +01:00
self . knownsecretlabel = WWLabel ( _ ( ' If you still have your OTP secret, then authenticate below to finalize wallet creation ' ) )
exist_otp_layout . addWidget ( self . knownsecretlabel )
2023-08-03 20:43:16 +02:00
self . exist_otp . setLayout ( exist_otp_layout )
self . authlabelnew = WWLabel ( _ ( ' Then, enter your Google Authenticator code: ' ) )
self . authlabelexist = WWLabel ( _ ( ' Google Authenticator code: ' ) )
2023-08-04 13:03:21 +02:00
self . spinner = QMovie ( icon_path ( ' spinner.gif ' ) )
self . spinner . setScaledSize ( QSize ( 24 , 24 ) )
self . spinner . setBackgroundColor ( QColor ( ' black ' ) )
self . spinner_l = QLabel ( )
self . spinner_l . setMargin ( 5 )
self . spinner_l . setVisible ( False )
self . spinner_l . setMovie ( self . spinner )
self . otp_status_l = QLabel ( )
2024-09-05 16:20:01 +00:00
self . otp_status_l . setAlignment ( Qt . AlignmentFlag . AlignHCenter )
2023-08-04 13:03:21 +02:00
self . otp_status_l . setVisible ( False )
2023-08-03 20:43:16 +02:00
self . resetlabel = WWLabel ( _ ( ' If you have lost your OTP secret, click the button below to request a new secret from the server. ' ) )
self . button = QPushButton ( ' Request OTP secret ' )
self . button . clicked . connect ( self . on_request_otp )
hbox = QHBoxLayout ( )
hbox . addWidget ( self . authlabelnew )
hbox . addWidget ( self . authlabelexist )
2023-08-04 13:03:21 +02:00
hbox . addStretch ( 1 )
hbox . addWidget ( self . spinner_l )
self . otp_e = AmountEdit ( None , is_int = True )
2024-09-18 17:39:46 +00:00
self . otp_e . setFocus ( )
2023-08-04 13:03:21 +02:00
self . otp_e . setMaximumWidth ( 150 )
self . otp_e . textEdited . connect ( self . on_otp_edited )
hbox . addWidget ( self . otp_e )
2023-08-03 20:43:16 +02:00
self . layout ( ) . addWidget ( self . new_otp )
self . layout ( ) . addWidget ( self . exist_otp )
self . layout ( ) . addLayout ( hbox )
2023-08-04 13:03:21 +02:00
self . layout ( ) . addWidget ( self . otp_status_l )
2023-08-03 20:43:16 +02:00
self . layout ( ) . addWidget ( self . resetlabel )
self . layout ( ) . addWidget ( self . button )
self . layout ( ) . addStretch ( 1 )
def on_ready ( self ) :
2023-08-24 15:31:47 +02:00
self . wizard . trustedcoin_qhelper . busyChanged . connect ( self . on_busy_changed )
self . wizard . trustedcoin_qhelper . remoteKeyError . connect ( self . on_remote_key_error )
self . wizard . trustedcoin_qhelper . otpSuccess . connect ( self . on_otp_success )
self . wizard . trustedcoin_qhelper . otpError . connect ( self . on_otp_error )
self . wizard . trustedcoin_qhelper . remoteKeyError . connect ( self . on_remote_key_error )
2023-08-04 13:03:21 +02:00
2024-01-15 17:45:29 +01:00
self . _is_online_continuation = ' seed ' not in self . wizard_data
if self . _is_online_continuation :
self . knownsecretlabel . setText ( _ ( ' Authenticate below to finalize wallet creation ' ) )
2023-12-08 11:56:11 +01:00
self . wizard . trustedcoin_qhelper . createKeystore ( )
2023-08-03 20:43:16 +02:00
def update ( self ) :
2023-08-24 15:31:47 +02:00
is_new = bool ( self . wizard . trustedcoin_qhelper . remoteKeyState != ' wallet_known ' )
2023-08-03 20:43:16 +02:00
self . new_otp . setVisible ( is_new )
self . exist_otp . setVisible ( not is_new )
self . authlabelnew . setVisible ( is_new )
self . authlabelexist . setVisible ( not is_new )
2024-01-16 12:30:24 +01:00
self . authlabelexist . setEnabled ( not self . _otp_verified )
self . otp_e . setEnabled ( not self . _otp_verified )
2024-01-15 17:45:29 +01:00
self . resetlabel . setVisible ( not is_new and not self . _otp_verified and not self . _is_online_continuation )
self . button . setVisible ( not is_new and not self . _otp_verified and not self . _is_online_continuation )
2023-08-03 20:43:16 +02:00
2023-08-24 15:31:47 +02:00
if self . wizard . trustedcoin_qhelper . otpSecret :
self . secretlabel . setText ( self . wizard . trustedcoin_qhelper . otpSecret )
2023-08-03 20:43:16 +02:00
uri = ' otpauth://totp/Electrum 2FA %s ?secret= %s &digits=6 ' % (
2024-05-28 15:04:22 +00:00
os . path . basename ( self . wizard_data [ ' wallet_name ' ] ) , self . wizard . trustedcoin_qhelper . otpSecret )
2023-08-03 20:43:16 +02:00
self . qr . setData ( uri )
def on_busy_changed ( self ) :
2023-08-24 15:31:47 +02:00
if not self . wizard . trustedcoin_qhelper . _verifyingOtp :
self . busy = self . wizard . trustedcoin_qhelper . busy
2023-08-04 13:03:21 +02:00
if not self . busy :
self . update ( )
2023-08-03 20:43:16 +02:00
def on_remote_key_error ( self , text ) :
self . _logger . error ( text )
self . error = text
def on_request_otp ( self ) :
2023-08-04 13:03:21 +02:00
self . otp_status_l . setVisible ( False )
2023-08-24 15:31:47 +02:00
self . wizard . trustedcoin_qhelper . resetOtpSecret ( )
2023-08-03 20:43:16 +02:00
self . update ( )
2023-08-04 13:03:21 +02:00
def on_otp_success ( self ) :
self . _otp_verified = True
self . otp_status_l . setText ( ' Valid! ' )
self . otp_status_l . setVisible ( True )
self . otp_status_l . setStyleSheet ( ColorScheme . GREEN . as_stylesheet ( False ) )
self . setEnabled ( True )
self . spinner_l . setVisible ( False )
self . spinner . stop ( )
self . valid = True
def on_otp_error ( self , message ) :
self . otp_status_l . setText ( message )
self . otp_status_l . setVisible ( True )
self . otp_status_l . setStyleSheet ( ColorScheme . RED . as_stylesheet ( False ) )
self . setEnabled ( True )
self . spinner_l . setVisible ( False )
self . spinner . stop ( )
def on_otp_edited ( self ) :
self . otp_status_l . setVisible ( False )
text = self . otp_e . text ( )
2024-02-21 13:31:06 +01:00
if len ( text ) > 0 :
try :
otp_int = int ( text )
except ValueError :
return
2023-08-04 13:03:21 +02:00
if len ( text ) == 6 :
# verify otp
2024-02-21 13:31:06 +01:00
self . wizard . trustedcoin_qhelper . checkOtp ( self . wizard . trustedcoin_qhelper . shortId , otp_int )
2023-08-04 13:03:21 +02:00
self . setEnabled ( False )
self . spinner_l . setVisible ( True )
self . spinner . start ( )
self . otp_e . setText ( ' ' )
2023-08-03 20:43:16 +02:00
def apply ( self ) :
pass
2024-10-21 16:14:12 +02:00
class WCKeepDisable ( WalletWizardComponent ) :
2023-08-03 20:43:16 +02:00
def __init__ ( self , parent , wizard ) :
2024-10-21 16:14:12 +02:00
WalletWizardComponent . __init__ ( self , parent , wizard , title = _ ( ' Restore 2FA wallet ' ) )
2023-08-03 20:43:16 +02:00
message = ' ' . join ( [
' You are going to restore a wallet protected with two-factor authentication. ' ,
' Do you want to keep using two-factor authentication with this wallet, ' ,
' or do you want to disable it, and have two master private keys in your wallet? '
] )
choices = [
( ' keep ' , _ ( ' Keep ' ) ) ,
( ' disable ' , _ ( ' Disable ' ) ) ,
]
2023-08-14 16:22:19 +02:00
self . choice_w = ChoiceWidget ( message = message , choices = choices )
self . layout ( ) . addWidget ( self . choice_w )
2023-08-03 20:43:16 +02:00
self . layout ( ) . addStretch ( 1 )
self . _valid = True
def apply ( self ) :
2024-02-05 14:39:03 +01:00
self . wizard_data [ ' trustedcoin_keepordisable ' ] = self . choice_w . selected_key
2023-08-28 16:22:37 +02:00
2024-10-21 16:14:12 +02:00
class WCContinueOnline ( WalletWizardComponent ) :
2023-08-28 16:22:37 +02:00
def __init__ ( self , parent , wizard ) :
2024-10-21 16:14:12 +02:00
WalletWizardComponent . __init__ ( self , parent , wizard , title = _ ( ' Continue Online ' ) )
2024-10-21 14:11:18 +02:00
self . cb_online = QCheckBox ( _ ( ' Go online to complete wallet creation ' ) )
2023-08-28 16:22:37 +02:00
def on_ready ( self ) :
path = os . path . join ( os . path . dirname ( self . wizard . _daemon . config . get_wallet_path ( ) ) , self . wizard_data [ ' wallet_name ' ] )
msg = [
_ ( " Your wallet file is: {} . " ) . format ( path ) ,
_ ( " You need to be online in order to complete the creation of "
" your wallet. If you want to continue online, keep the checkbox "
" checked and press Next. " ) ,
_ ( " If you want this system to stay offline "
" and continue the completion of the wallet on an online system, "
" uncheck the checkbox and press Finish. " )
]
self . layout ( ) . addWidget ( WWLabel ( ' \n \n ' . join ( msg ) ) )
self . layout ( ) . addStretch ( 1 )
self . cb_online . setChecked ( True )
self . cb_online . stateChanged . connect ( self . on_updated )
# self.cb_online.setToolTip(_("Check this box to request a new secret. You will need to retype your seed."))
self . layout ( ) . addWidget ( self . cb_online )
2024-09-05 16:20:01 +00:00
self . layout ( ) . setAlignment ( self . cb_online , Qt . AlignmentFlag . AlignHCenter )
2023-08-28 16:22:37 +02:00
self . layout ( ) . addStretch ( 1 )
self . _valid = True
def apply ( self ) :
self . wizard_data [ ' trustedcoin_go_online ' ] = self . cb_online . isChecked ( )
2024-10-08 00:04:20 +02:00
2024-10-21 16:14:12 +02:00
class WCKeystorePassword ( WalletWizardComponent ) :
2024-10-08 00:04:20 +02:00
def __init__ ( self , parent , wizard ) :
2024-10-21 16:14:12 +02:00
WalletWizardComponent . __init__ ( self , parent , wizard , title = _ ( ' Unlock Keystore ' ) )
2024-10-08 00:04:20 +02:00
self . layout ( ) . addStretch ( 1 )
hbox2 = QHBoxLayout ( )
hbox2 . addStretch ( 1 )
self . pw_e = PasswordLineEdit ( ' ' , self )
self . pw_e . setFixedWidth ( 17 * char_width_in_lineedit ( ) )
self . pw_e . textEdited . connect ( self . on_text )
pw_label = QLabel ( _ ( ' Password ' ) + ' : ' )
hbox2 . addWidget ( pw_label )
hbox2 . addWidget ( self . pw_e )
hbox2 . addStretch ( 1 )
self . layout ( ) . addLayout ( hbox2 )
self . layout ( ) . addStretch ( 1 )
2024-10-21 14:11:18 +02:00
self . ks = None
2024-10-08 00:04:20 +02:00
2024-10-21 14:11:18 +02:00
def on_ready ( self ) :
2024-10-08 00:04:20 +02:00
self . ks = self . wizard_data [ ' xprv1 ' ]
def on_text ( self ) :
try :
self . ks . check_password ( self . pw_e . text ( ) )
except InvalidPassword :
self . valid = False
return
self . valid = True
def apply ( self ) :
if self . valid :
self . wizard_data [ ' xprv1 ' ] = self . ks . get_master_private_key ( self . pw_e . text ( ) )
self . wizard_data [ ' password ' ] = self . pw_e . text ( )