@ -62,6 +62,7 @@ from electrum.util import (format_time, format_satoshis, format_fee_satoshis,
UnknownBaseUnit , DECIMAL_POINT_DEFAULT , UserFacingException ,
get_new_wallet_name , send_exception_to_crash_reporter ,
InvalidBitcoinURI , InvoiceError )
from electrum . util import PR_TYPE_ONCHAIN , PR_TYPE_LN
from electrum . lnutil import PaymentFailure , SENT , RECEIVED
from electrum . transaction import Transaction , TxOutput
from electrum . address_synchronizer import AddTransactionException
@ -142,7 +143,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
assert wallet , " no wallet "
self . wallet = wallet
self . fx = gui_object . daemon . fx # type: FxThread
#self.invoices = wallet.invoices
self . contacts = wallet . contacts
self . tray = gui_object . tray
self . app = gui_object . app
@ -171,6 +171,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . completions = QStringListModel ( )
self . send_tab_is_onchain = False
self . tabs = tabs = QTabWidget ( self )
self . send_tab = self . create_send_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 . 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
self . request_list = RequestList ( self )
@ -1076,6 +1078,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . address_list . update ( )
self . request_list . update ( )
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 ) :
addr = self . wallet . get_unused_address ( )
@ -1206,34 +1211,34 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . message_e = MyLineEdit ( )
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 ' \
+ _ ( ' 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 ' \
+ _ ( ' Keyboard shortcut: type " ! " to send all your coins. ' )
amount_label = HelpLabel ( _ ( ' Amount ' ) , msg )
grid . addWidget ( amount_label , 4 , 0 )
grid . addWidget ( self . amount_e , 4 , 1 )
grid . addWidget ( amount_label , 3 , 0 )
grid . addWidget ( self . amount_e , 3 , 1 )
self . fiat_send_e = AmountEdit ( self . fx . get_currency if self . fx else ' ' )
if not self . fx or not self . fx . is_enabled ( ) :
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 (
lambda : self . fiat_send_e . setFrozen ( self . amount_e . isReadOnly ( ) ) )
self . max_button = EnterButton ( _ ( " Max " ) , self . spend_max )
self . max_button . setFixedWidth ( self . amount_e . width ( ) )
self . max_button . setCheckable ( True )
grid . addWidget ( self . max_button , 4 , 3 )
grid . addWidget ( self . max_button , 3 , 3 )
hbox = QHBoxLayout ( )
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 ' \
+ _ ( ' 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 ) :
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 . 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 )
buttons = QHBoxLayout ( )
buttons . addStretch ( 1 )
buttons . addWidget ( self . save_button )
buttons . addWidget ( self . clear_button )
buttons . addWidget ( self . preview_button )
buttons . addWidget ( self . send_button )
@ -1355,7 +1362,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
def reset_max ( text ) :
self . max_button . setChecked ( False )
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 . fiat_send_e . textEdited . connect ( reset_max )
@ -1398,7 +1405,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . fee_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
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
still build the TX to see if there are enough funds .
'''
if self . payto_e . is_lightning :
if not self . is_onchain :
return
freeze_fee = self . is_send_fee_frozen ( )
freeze_feerate = self . is_send_feerate_frozen ( )
@ -1448,7 +1457,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . statusBar ( ) . showMessage ( ' ' )
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 :
_type , addr = self . get_payto_or_dummy ( )
outputs = [ TxOutput ( _type , addr , amount ) ]
@ -1607,15 +1619,12 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
fee_estimator = None
return fee_estimator
def read_send_tab ( self ) :
label = self . message_e . text ( )
def read_outputs ( self ) :
if self . payment_request :
outputs = self . payment_request . get_outputs ( )
else :
outputs = self . payto_e . get_outputs ( self . max_button . isChecked ( ) )
fee_estimator = self . get_send_fee_estimator ( )
coins = self . get_coins ( )
return outputs , fee_estimator , label , coins
return outputs
def check_send_tab_outputs_and_show_errors ( self , outputs ) - > bool :
""" Returns whether there are errors with outputs.
@ -1658,9 +1667,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
return False # no errors
def do_preview ( self ) :
self . do_send ( preview = True )
def pay_lightning_invoice ( self , invoice ) :
amount_sat = self . amount_e . get_amount ( )
attempts = LN_NUM_PAYMENT_ATTEMPTS
@ -1684,15 +1690,60 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
e = args [ 0 ]
self . show_error ( _ ( ' Error ' ) + ' \n ' + str ( e ) )
def do_send ( self , preview = False ) :
if self . payto_e . is_lightning :
def read_invoice ( self ) :
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 )
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 ) :
return
outputs , fee_estimator , tx_desc , coins = self . read_send_tab ( )
if self . check_send_tab_outputs_and_show_errors ( outputs ) :
return
outputs = [ TxOutput ( * x ) for x in outputs ]
fee_estimator = self . get_send_fee_estimator ( )
coins = self . get_coins ( )
try :
is_sweep = bool ( self . tx_external_keypairs )
tx = self . wallet . make_unsigned_transaction (
@ -1724,7 +1775,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
return
if preview :
self . show_transaction ( tx , tx_desc )
self . show_transaction ( tx , message )
return
if not self . network :
@ -1764,7 +1815,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . show_transaction ( tx )
self . do_clear ( )
else :
self . broadcast_transaction ( tx , tx_desc )
self . broadcast_transaction ( tx , message )
self . sign_tx_with_password ( tx , sign_done , password )
@protected
@ -1935,8 +1986,13 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
if lnaddr . amount is not None :
self . amount_e . setAmount ( lnaddr . amount * COIN )
#self.amount_e.textEdited.emit("")
self . payto_e . is_lightning = True
self . show_send_tab_onchain_fees ( False )
self . set_onchain ( 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 ) :
self . feecontrol_fields . setVisible ( b )
@ -1951,6 +2007,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . show_error ( _ ( " Error parsing URI " ) + f " : \n { e } " )
return
self . show_send_tab ( )
self . payto_URI = out
r = out . get ( ' r ' )
sig = out . get ( ' sig ' )
name = out . get ( ' name ' )
@ -1977,9 +2034,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . max_button . setChecked ( False )
self . not_enough_funds = False
self . payment_request = None
self . payto_URI = None
self . payto_e . is_pr = False
self . payto_e . is_lightning = False
self . show_send_tab_onchain_fees ( Tru e )
self . is_onchain = False
self . set_onchain ( Fals e )
for e in [ self . payto_e , self . message_e , self . amount_e , self . fiat_send_e ,
self . fee_e , self . feerate_e ] :
e . setText ( ' ' )
@ -1993,6 +2051,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . update_status ( )
run_hook ( ' do_clear ' , self )
def set_frozen_state_of_addresses ( self , addrs , freeze : bool ) :
self . wallet . set_frozen_state_of_addresses ( addrs , freeze )
self . address_list . update ( )
@ -2048,6 +2107,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
def spend_coins ( self , coins ) :
self . set_pay_from ( coins )
self . set_onchain ( len ( coins ) > 0 )
self . show_send_tab ( )
self . update_fee ( )
@ -2095,16 +2155,19 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
self . update_completions ( )
def show_invoice ( self , key ) :
pr = self . wallet . get_invoice ( key )
if pr is None :
invoice = self . wallet . get_invoice ( key )
if invoice is None :
self . show_error ( ' Cannot find payment request in wallet. ' )
return
pr . verify ( self . contacts )
self . show_pr_details ( pr )
bip70 = invoice . get ( ' bip70 ' )
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 ( )
d = WindowModalDialog ( self , _ ( " Invoice " ) )
d = WindowModalDialog ( self , _ ( " BIP70 Invoice" ) )
vbox = QVBoxLayout ( d )
grid = QGridLayout ( )
grid . addWidget ( QLabel ( _ ( " Requestor " ) + ' : ' ) , 0 , 0 )
@ -2140,7 +2203,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
vbox . addLayout ( Buttons ( exportButton , deleteButton , CloseButton ( d ) ) )
d . exec_ ( )
def do_ pay_invoice( self , key ) :
def pay_bip70 _invoice ( self , key ) :
pr = self . wallet . get_invoice ( key )
self . payment_request = pr
self . prepare_for_payment_request ( )