@ -62,6 +62,7 @@ from electrum.util import (format_time, format_satoshis, format_fee_satoshis,
UnknownBaseUnit , DECIMAL_POINT_DEFAULT , UserFacingException ,
UnknownBaseUnit , DECIMAL_POINT_DEFAULT , UserFacingException ,
get_new_wallet_name , send_exception_to_crash_reporter ,
get_new_wallet_name , send_exception_to_crash_reporter ,
InvalidBitcoinURI , InvoiceError )
InvalidBitcoinURI , InvoiceError )
from electrum . util import PR_TYPE_ONCHAIN , PR_TYPE_LN
from electrum . lnutil import PaymentFailure , SENT , RECEIVED
from electrum . lnutil import PaymentFailure , SENT , RECEIVED
from electrum . transaction import Transaction , TxOutput
from electrum . transaction import Transaction , TxOutput
from electrum . address_synchronizer import AddTransactionException
from electrum . address_synchronizer import AddTransactionException
@ -142,7 +143,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
assert wallet , " no wallet "
assert wallet , " no wallet "
self . wallet = wallet
self . wallet = wallet
self . fx = gui_object . daemon . fx # type: FxThread
self . fx = gui_object . daemon . fx # type: FxThread
#self.invoices = wallet.invoices
self . contacts = wallet . contacts
self . contacts = wallet . contacts
self . tray = gui_object . tray
self . tray = gui_object . tray
self . app = gui_object . app
self . app = gui_object . app
@ -171,6 +171,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . completions = QStringListModel ( )
self . completions = QStringListModel ( )
self . send_tab_is_onchain = False
self . tabs = tabs = QTabWidget ( self )
self . tabs = tabs = QTabWidget ( self )
self . send_tab = self . create_send_tab ( )
self . send_tab = self . create_send_tab ( )
self . receive_tab = self . create_receive_tab ( )
self . receive_tab = self . create_receive_tab ( )
@ -1001,7 +1003,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . receive_qr . enterEvent = lambda x : self . app . setOverrideCursor ( QCursor ( Qt . PointingHandCursor ) )
self . receive_qr . enterEvent = lambda x : self . app . setOverrideCursor ( QCursor ( Qt . PointingHandCursor ) )
self . receive_qr . leaveEvent = lambda x : self . app . setOverrideCursor ( QCursor ( Qt . ArrowCursor ) )
self . receive_qr . leaveEvent = lambda x : self . app . setOverrideCursor ( QCursor ( Qt . ArrowCursor ) )
self . receive_requests_label = QLabel ( _ ( ' Incoming invoice s ' ) )
self . receive_requests_label = QLabel ( _ ( ' Incoming payment s ' ) )
from . request_list import RequestList
from . request_list import RequestList
self . request_list = RequestList ( self )
self . request_list = RequestList ( self )
@ -1076,6 +1078,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . address_list . update ( )
self . address_list . update ( )
self . request_list . update ( )
self . request_list . update ( )
self . request_list . select_key ( key )
self . request_list . select_key ( key )
# clear request fields
self . receive_amount_e . setText ( ' ' )
self . receive_message_e . setText ( ' ' )
def create_bitcoin_request ( self , amount , message , expiration ) :
def create_bitcoin_request ( self , amount , message , expiration ) :
addr = self . wallet . get_unused_address ( )
addr = self . wallet . get_unused_address ( )
@ -1206,34 +1211,34 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . message_e = MyLineEdit ( )
self . message_e = MyLineEdit ( )
grid . addWidget ( self . message_e , 2 , 1 , 1 , - 1 )
grid . addWidget ( self . message_e , 2 , 1 , 1 , - 1 )
self . from_label = QLabel ( _ ( ' From ' ) )
grid . addWidget ( self . from_label , 3 , 0 )
self . from_list = FromList ( self , self . from_list_menu )
grid . addWidget ( self . from_list , 3 , 1 , 1 , - 1 )
self . set_pay_from ( [ ] )
msg = _ ( ' Amount to be sent. ' ) + ' \n \n ' \
msg = _ ( ' Amount to be sent. ' ) + ' \n \n ' \
+ _ ( ' The amount will be displayed in red if you do not have enough funds in your wallet. ' ) + ' ' \
+ _ ( ' The amount will be displayed in red if you do not have enough funds in your wallet. ' ) + ' ' \
+ _ ( ' Note that if you have frozen some of your addresses, the available funds will be lower than your total balance. ' ) + ' \n \n ' \
+ _ ( ' Note that if you have frozen some of your addresses, the available funds will be lower than your total balance. ' ) + ' \n \n ' \
+ _ ( ' Keyboard shortcut: type " ! " to send all your coins. ' )
+ _ ( ' Keyboard shortcut: type " ! " to send all your coins. ' )
amount_label = HelpLabel ( _ ( ' Amount ' ) , msg )
amount_label = HelpLabel ( _ ( ' Amount ' ) , msg )
grid . addWidget ( amount_label , 4 , 0 )
grid . addWidget ( amount_label , 3 , 0 )
grid . addWidget ( self . amount_e , 4 , 1 )
grid . addWidget ( self . amount_e , 3 , 1 )
self . fiat_send_e = AmountEdit ( self . fx . get_currency if self . fx else ' ' )
self . fiat_send_e = AmountEdit ( self . fx . get_currency if self . fx else ' ' )
if not self . fx or not self . fx . is_enabled ( ) :
if not self . fx or not self . fx . is_enabled ( ) :
self . fiat_send_e . setVisible ( False )
self . fiat_send_e . setVisible ( False )
grid . addWidget ( self . fiat_send_e , 4 , 2 )
grid . addWidget ( self . fiat_send_e , 3 , 2 )
self . amount_e . frozen . connect (
self . amount_e . frozen . connect (
lambda : self . fiat_send_e . setFrozen ( self . amount_e . isReadOnly ( ) ) )
lambda : self . fiat_send_e . setFrozen ( self . amount_e . isReadOnly ( ) ) )
self . max_button = EnterButton ( _ ( " Max " ) , self . spend_max )
self . max_button = EnterButton ( _ ( " Max " ) , self . spend_max )
self . max_button . setFixedWidth ( self . amount_e . width ( ) )
self . max_button . setFixedWidth ( self . amount_e . width ( ) )
self . max_button . setCheckable ( True )
self . max_button . setCheckable ( True )
grid . addWidget ( self . max_button , 4 , 3 )
grid . addWidget ( self . max_button , 3 , 3 )
hbox = QHBoxLayout ( )
hbox = QHBoxLayout ( )
hbox . addStretch ( 1 )
hbox . addStretch ( 1 )
grid . addLayout ( hbox , 4 , 4 )
grid . addLayout ( hbox , 3 , 4 )
self . from_label = QLabel ( _ ( ' From ' ) )
grid . addWidget ( self . from_label , 4 , 0 )
self . from_list = FromList ( self , self . from_list_menu )
grid . addWidget ( self . from_list , 4 , 1 , 1 , - 1 )
self . set_pay_from ( [ ] )
msg = _ ( ' Bitcoin transactions are in general not free. A transaction fee is paid by the sender of the funds. ' ) + ' \n \n ' \
msg = _ ( ' Bitcoin transactions are in general not free. A transaction fee is paid by the sender of the funds. ' ) + ' \n \n ' \
+ _ ( ' The amount of fee can be decided freely by the sender. However, transactions with low fees take more time to be processed. ' ) + ' \n \n ' \
+ _ ( ' The amount of fee can be decided freely by the sender. However, transactions with low fees take more time to be processed. ' ) + ' \n \n ' \
@ -1337,12 +1342,14 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
if not self . config . get ( ' show_fee ' , False ) :
if not self . config . get ( ' show_fee ' , False ) :
self . fee_adv_controls . setVisible ( False )
self . fee_adv_controls . setVisible ( False )
self . save_button = EnterButton ( _ ( " Save " ) , self . do_save_invoice )
self . preview_button = EnterButton ( _ ( " Preview " ) , self . do_preview )
self . preview_button = EnterButton ( _ ( " Preview " ) , self . do_preview )
self . preview_button . setToolTip ( _ ( ' Display the details of your transaction before signing it. ' ) )
self . preview_button . setToolTip ( _ ( ' Display the details of your transaction before signing it. ' ) )
self . send_button = EnterButton ( _ ( " Send " ) , self . do_send )
self . send_button = EnterButton ( _ ( " Send " ) , self . do_pay )
self . clear_button = EnterButton ( _ ( " Clear " ) , self . do_clear )
self . clear_button = EnterButton ( _ ( " Clear " ) , self . do_clear )
buttons = QHBoxLayout ( )
buttons = QHBoxLayout ( )
buttons . addStretch ( 1 )
buttons . addStretch ( 1 )
buttons . addWidget ( self . save_button )
buttons . addWidget ( self . clear_button )
buttons . addWidget ( self . clear_button )
buttons . addWidget ( self . preview_button )
buttons . addWidget ( self . preview_button )
buttons . addWidget ( self . send_button )
buttons . addWidget ( self . send_button )
@ -1355,7 +1362,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
def reset_max ( text ) :
def reset_max ( text ) :
self . max_button . setChecked ( False )
self . max_button . setChecked ( False )
enable = not bool ( text ) and not self . amount_e . isReadOnly ( )
enable = not bool ( text ) and not self . amount_e . isReadOnly ( )
self . max_button . setEnabled ( enable )
#self.max_button.setEnabled(enable )
self . amount_e . textEdited . connect ( reset_max )
self . amount_e . textEdited . connect ( reset_max )
self . fiat_send_e . textEdited . connect ( reset_max )
self . fiat_send_e . textEdited . connect ( reset_max )
@ -1398,7 +1405,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . fee_e . textChanged . connect ( entry_changed )
self . fee_e . textChanged . connect ( entry_changed )
self . feerate_e . textChanged . connect ( entry_changed )
self . feerate_e . textChanged . connect ( entry_changed )
self . invoices_label = QLabel ( _ ( ' Outgoing invoices ' ) )
self . set_onchain ( False )
self . invoices_label = QLabel ( _ ( ' Outgoing payments ' ) )
from . invoice_list import InvoiceList
from . invoice_list import InvoiceList
self . invoice_list = InvoiceList ( self )
self . invoice_list = InvoiceList ( self )
@ -1436,7 +1445,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
''' Recalculate the fee. If the fee was manually input, retain it, but
''' Recalculate the fee. If the fee was manually input, retain it, but
still build the TX to see if there are enough funds .
still build the TX to see if there are enough funds .
'''
'''
if self . payto_e . is_lightning :
if not self . is_onchain :
return
return
freeze_fee = self . is_send_fee_frozen ( )
freeze_fee = self . is_send_fee_frozen ( )
freeze_feerate = self . is_send_feerate_frozen ( )
freeze_feerate = self . is_send_feerate_frozen ( )
@ -1448,7 +1457,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . statusBar ( ) . showMessage ( ' ' )
self . statusBar ( ) . showMessage ( ' ' )
return
return
outputs , fee_estimator , tx_desc , coins = self . read_send_tab ( )
outputs = self . read_outputs ( )
fee_estimator = self . get_send_fee_estimator ( )
coins = self . get_coins ( )
if not outputs :
if not outputs :
_type , addr = self . get_payto_or_dummy ( )
_type , addr = self . get_payto_or_dummy ( )
outputs = [ TxOutput ( _type , addr , amount ) ]
outputs = [ TxOutput ( _type , addr , amount ) ]
@ -1607,15 +1619,12 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
fee_estimator = None
fee_estimator = None
return fee_estimator
return fee_estimator
def read_send_tab ( self ) :
def read_outputs ( self ) :
label = self . message_e . text ( )
if self . payment_request :
if self . payment_request :
outputs = self . payment_request . get_outputs ( )
outputs = self . payment_request . get_outputs ( )
else :
else :
outputs = self . payto_e . get_outputs ( self . max_button . isChecked ( ) )
outputs = self . payto_e . get_outputs ( self . max_button . isChecked ( ) )
fee_estimator = self . get_send_fee_estimator ( )
return outputs
coins = self . get_coins ( )
return outputs , fee_estimator , label , coins
def check_send_tab_outputs_and_show_errors ( self , outputs ) - > bool :
def check_send_tab_outputs_and_show_errors ( self , outputs ) - > bool :
""" Returns whether there are errors with outputs.
""" Returns whether there are errors with outputs.
@ -1658,9 +1667,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
return False # no errors
return False # no errors
def do_preview ( self ) :
self . do_send ( preview = True )
def pay_lightning_invoice ( self , invoice ) :
def pay_lightning_invoice ( self , invoice ) :
amount_sat = self . amount_e . get_amount ( )
amount_sat = self . amount_e . get_amount ( )
attempts = LN_NUM_PAYMENT_ATTEMPTS
attempts = LN_NUM_PAYMENT_ATTEMPTS
@ -1684,15 +1690,60 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
e = args [ 0 ]
e = args [ 0 ]
self . show_error ( _ ( ' Error ' ) + ' \n ' + str ( e ) )
self . show_error ( _ ( ' Error ' ) + ' \n ' + str ( e ) )
def do_send ( self , preview = False ) :
def read_invoice ( self ) :
if self . payto_e . is_lightning :
message = self . message_e . text ( )
amount = self . amount_e . get_amount ( )
if not self . is_onchain :
return {
' type ' : PR_TYPE_LN ,
' invoice ' : self . payto_e . lightning_invoice ,
' amount ' : amount ,
' message ' : message ,
}
else :
outputs = self . read_outputs ( )
if self . check_send_tab_outputs_and_show_errors ( outputs ) :
return
return self . wallet . create_invoice ( outputs , message , self . payment_request , self . payto_URI )
def do_save_invoice ( self ) :
invoice = self . read_invoice ( )
if not invoice :
return
self . wallet . save_invoice ( invoice )
self . do_clear ( )
self . invoice_list . update ( )
def do_preview ( self ) :
self . do_pay ( preview = True )
def do_pay ( self , preview = False ) :
invoice = self . read_invoice ( )
if not invoice :
return
if not preview :
self . wallet . save_invoice ( invoice )
self . do_clear ( )
self . invoice_list . update ( )
self . do_pay_invoice ( invoice , preview )
def do_pay_invoice ( self , invoice , preview = False ) :
if invoice [ ' type ' ] == PR_TYPE_LN :
self . pay_lightning_invoice ( self . payto_e . lightning_invoice )
self . pay_lightning_invoice ( self . payto_e . lightning_invoice )
return
return
elif invoice [ ' type ' ] == PR_TYPE_ONCHAIN :
message = invoice [ ' message ' ]
outputs = invoice [ ' outputs ' ]
amount = sum ( map ( lambda x : x [ 2 ] , outputs ) )
else :
raise Exception ( ' unknowwn invoicce type ' )
if run_hook ( ' abort_send ' , self ) :
if run_hook ( ' abort_send ' , self ) :
return
return
outputs , fee_estimator , tx_desc , coins = self . read_send_tab ( )
if self . check_send_tab_outputs_and_show_errors ( outputs ) :
outputs = [ TxOutput ( * x ) for x in outputs ]
return
fee_estimator = self . get_send_fee_estimator ( )
coins = self . get_coins ( )
try :
try :
is_sweep = bool ( self . tx_external_keypairs )
is_sweep = bool ( self . tx_external_keypairs )
tx = self . wallet . make_unsigned_transaction (
tx = self . wallet . make_unsigned_transaction (
@ -1724,7 +1775,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
return
return
if preview :
if preview :
self . show_transaction ( tx , tx_desc )
self . show_transaction ( tx , message )
return
return
if not self . network :
if not self . network :
@ -1764,7 +1815,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . show_transaction ( tx )
self . show_transaction ( tx )
self . do_clear ( )
self . do_clear ( )
else :
else :
self . broadcast_transaction ( tx , tx_desc )
self . broadcast_transaction ( tx , message )
self . sign_tx_with_password ( tx , sign_done , password )
self . sign_tx_with_password ( tx , sign_done , password )
@protected
@protected
@ -1935,8 +1986,13 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
if lnaddr . amount is not None :
if lnaddr . amount is not None :
self . amount_e . setAmount ( lnaddr . amount * COIN )
self . amount_e . setAmount ( lnaddr . amount * COIN )
#self.amount_e.textEdited.emit("")
#self.amount_e.textEdited.emit("")
self . payto_e . is_lightning = True
self . set_onchain ( False )
self . show_send_tab_onchain_fees ( False )
def set_onchain ( self , b ) :
self . is_onchain = b
self . preview_button . setEnabled ( b )
self . max_button . setEnabled ( b )
self . show_send_tab_onchain_fees ( b )
def show_send_tab_onchain_fees ( self , b : bool ) :
def show_send_tab_onchain_fees ( self , b : bool ) :
self . feecontrol_fields . setVisible ( b )
self . feecontrol_fields . setVisible ( b )
@ -1951,6 +2007,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . show_error ( _ ( " Error parsing URI " ) + f " : \n { e } " )
self . show_error ( _ ( " Error parsing URI " ) + f " : \n { e } " )
return
return
self . show_send_tab ( )
self . show_send_tab ( )
self . payto_URI = out
r = out . get ( ' r ' )
r = out . get ( ' r ' )
sig = out . get ( ' sig ' )
sig = out . get ( ' sig ' )
name = out . get ( ' name ' )
name = out . get ( ' name ' )
@ -1977,9 +2034,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . max_button . setChecked ( False )
self . max_button . setChecked ( False )
self . not_enough_funds = False
self . not_enough_funds = False
self . payment_request = None
self . payment_request = None
self . payto_URI = None
self . payto_e . is_pr = False
self . payto_e . is_pr = False
self . payto_e . is_lightning = False
self . is_onchain = False
self . show_send_tab_onchain_fees ( Tru e )
self . set_onchain ( Fals e )
for e in [ self . payto_e , self . message_e , self . amount_e , self . fiat_send_e ,
for e in [ self . payto_e , self . message_e , self . amount_e , self . fiat_send_e ,
self . fee_e , self . feerate_e ] :
self . fee_e , self . feerate_e ] :
e . setText ( ' ' )
e . setText ( ' ' )
@ -1993,6 +2051,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . update_status ( )
self . update_status ( )
run_hook ( ' do_clear ' , self )
run_hook ( ' do_clear ' , self )
def set_frozen_state_of_addresses ( self , addrs , freeze : bool ) :
def set_frozen_state_of_addresses ( self , addrs , freeze : bool ) :
self . wallet . set_frozen_state_of_addresses ( addrs , freeze )
self . wallet . set_frozen_state_of_addresses ( addrs , freeze )
self . address_list . update ( )
self . address_list . update ( )
@ -2048,6 +2107,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
def spend_coins ( self , coins ) :
def spend_coins ( self , coins ) :
self . set_pay_from ( coins )
self . set_pay_from ( coins )
self . set_onchain ( len ( coins ) > 0 )
self . show_send_tab ( )
self . show_send_tab ( )
self . update_fee ( )
self . update_fee ( )
@ -2095,16 +2155,19 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . update_completions ( )
self . update_completions ( )
def show_invoice ( self , key ) :
def show_invoice ( self , key ) :
pr = self . wallet . get_invoice ( key )
invoice = self . wallet . get_invoice ( key )
if pr is None :
if invoice is None :
self . show_error ( ' Cannot find payment request in wallet. ' )
self . show_error ( ' Cannot find payment request in wallet. ' )
return
return
pr . verify ( self . contacts )
bip70 = invoice . get ( ' bip70 ' )
self . show_pr_details ( pr )
if bip70 :
pr = paymentrequest . PaymentRequest ( bytes . fromhex ( bip70 ) )
pr . verify ( self . contacts )
self . show_bip70_details ( pr )
def show_pr_details ( self , pr ) :
def show_bip70 _details ( self , pr ) :
key = pr . get_id ( )
key = pr . get_id ( )
d = WindowModalDialog ( self , _ ( " Invoice " ) )
d = WindowModalDialog ( self , _ ( " BIP70 Invoice" ) )
vbox = QVBoxLayout ( d )
vbox = QVBoxLayout ( d )
grid = QGridLayout ( )
grid = QGridLayout ( )
grid . addWidget ( QLabel ( _ ( " Requestor " ) + ' : ' ) , 0 , 0 )
grid . addWidget ( QLabel ( _ ( " Requestor " ) + ' : ' ) , 0 , 0 )
@ -2140,7 +2203,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
vbox . addLayout ( Buttons ( exportButton , deleteButton , CloseButton ( d ) ) )
vbox . addLayout ( Buttons ( exportButton , deleteButton , CloseButton ( d ) ) )
d . exec_ ( )
d . exec_ ( )
def do_ pay_invoice( self , key ) :
def pay_bip70 _invoice ( self , key ) :
pr = self . wallet . get_invoice ( key )
pr = self . wallet . get_invoice ( key )
self . payment_request = pr
self . payment_request = pr
self . prepare_for_payment_request ( )
self . prepare_for_payment_request ( )