mirror of
https://github.com/LBRYFoundation/LBRY-Vault.git
synced 2025-08-23 17:47:31 +00:00
Kivy: Support Lightning in Send tab
This commit is contained in:
parent
f803bb571d
commit
1352b0ce9f
4 changed files with 88 additions and 25 deletions
|
@ -162,11 +162,16 @@ class ElectrumWindow(App):
|
|||
self.switch_to('send')
|
||||
self.send_screen.set_URI(uri)
|
||||
|
||||
def set_ln_invoice(self, invoice):
|
||||
self.switch_to('send')
|
||||
self.send_screen.set_ln_invoice(invoice)
|
||||
|
||||
def on_new_intent(self, intent):
|
||||
if intent.getScheme() != 'bitcoin':
|
||||
return
|
||||
uri = intent.getDataString()
|
||||
self.set_URI(uri)
|
||||
data = intent.getDataString()
|
||||
if intent.getScheme() == 'bitcoin':
|
||||
self.set_URI(data)
|
||||
elif intent.getScheme() == 'lightning':
|
||||
self.set_ln_invoice(data)
|
||||
|
||||
def on_language(self, instance, language):
|
||||
Logger.info('language: {}'.format(language))
|
||||
|
@ -355,6 +360,9 @@ class ElectrumWindow(App):
|
|||
if data.startswith('bitcoin:'):
|
||||
self.set_URI(data)
|
||||
return
|
||||
if data.startswith('ln'):
|
||||
self.set_ln_invoice(data)
|
||||
return
|
||||
# try to decode transaction
|
||||
from electrum.transaction import Transaction
|
||||
from electrum.util import bh2u
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import asyncio
|
||||
from weakref import ref
|
||||
from decimal import Decimal
|
||||
import re
|
||||
import datetime
|
||||
import traceback, sys
|
||||
from enum import Enum, auto
|
||||
|
||||
from kivy.app import App
|
||||
from kivy.cache import Cache
|
||||
|
@ -19,19 +21,26 @@ from kivy.factory import Factory
|
|||
from kivy.utils import platform
|
||||
|
||||
from electrum.util import profiler, parse_URI, format_time, InvalidPassword, NotEnoughFunds, Fiat
|
||||
from electrum import bitcoin
|
||||
from electrum import bitcoin, constants
|
||||
from electrum.transaction import TxOutput, Transaction, tx_from_str
|
||||
from electrum.util import send_exception_to_crash_reporter, parse_URI, InvalidBitcoinURI
|
||||
from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED
|
||||
from electrum.plugin import run_hook
|
||||
from electrum.wallet import InternalAddressCorruption
|
||||
from electrum import simple_config
|
||||
from electrum.lnaddr import lndecode
|
||||
from electrum.lnutil import RECEIVED, SENT
|
||||
|
||||
from .context_menu import ContextMenu
|
||||
|
||||
|
||||
from electrum.gui.kivy.i18n import _
|
||||
|
||||
class Destination(Enum):
|
||||
Address = auto()
|
||||
PR = auto()
|
||||
LN = auto()
|
||||
|
||||
class HistoryRecycleView(RecycleView):
|
||||
pass
|
||||
|
||||
|
@ -184,7 +193,19 @@ class SendScreen(CScreen):
|
|||
self.screen.message = uri.get('message', '')
|
||||
self.screen.amount = self.app.format_amount_and_units(amount) if amount else ''
|
||||
self.payment_request = None
|
||||
self.screen.is_pr = False
|
||||
self.screen.destinationtype = Destination.Address
|
||||
|
||||
def set_ln_invoice(self, invoice):
|
||||
try:
|
||||
lnaddr = lndecode(invoice, expected_hrp=constants.net.SEGWIT_HRP)
|
||||
except Exception as e:
|
||||
self.app.show_info(invoice + _(" is not a valid Lightning invoice: ") + repr(e)) # repr because str(Exception()) == ''
|
||||
return
|
||||
self.screen.address = invoice
|
||||
self.screen.message = dict(lnaddr.tags).get('d', None)
|
||||
self.screen.amount = self.app.format_amount_and_units(lnaddr.amount * bitcoin.COIN) if lnaddr.amount else ''
|
||||
self.payment_request = None
|
||||
self.screen.destinationtype = Destination.LN
|
||||
|
||||
def update(self):
|
||||
if self.app.wallet and self.payment_request_queued:
|
||||
|
@ -196,7 +217,7 @@ class SendScreen(CScreen):
|
|||
self.screen.message = ''
|
||||
self.screen.address = ''
|
||||
self.payment_request = None
|
||||
self.screen.is_pr = False
|
||||
self.screen.destinationtype = Destination.Address
|
||||
|
||||
def set_request(self, pr):
|
||||
self.screen.address = pr.get_requestor()
|
||||
|
@ -204,16 +225,16 @@ class SendScreen(CScreen):
|
|||
self.screen.amount = self.app.format_amount_and_units(amount) if amount else ''
|
||||
self.screen.message = pr.get_memo()
|
||||
if pr.is_pr():
|
||||
self.screen.is_pr = True
|
||||
self.screen.destinationtype = Destination.PR
|
||||
self.payment_request = pr
|
||||
else:
|
||||
self.screen.is_pr = False
|
||||
self.screen.destinationtype = Destination.Address
|
||||
self.payment_request = None
|
||||
|
||||
def do_save(self):
|
||||
if not self.screen.address:
|
||||
return
|
||||
if self.screen.is_pr:
|
||||
if self.screen.destinationtype == Destination.PR:
|
||||
# it should be already saved
|
||||
return
|
||||
# save address as invoice
|
||||
|
@ -226,10 +247,10 @@ class SendScreen(CScreen):
|
|||
self.app.wallet.invoices.add(pr)
|
||||
self.app.show_info(_("Invoice saved"))
|
||||
if pr.is_pr():
|
||||
self.screen.is_pr = True
|
||||
self.screen.destinationtype = Destination.PR
|
||||
self.payment_request = pr
|
||||
else:
|
||||
self.screen.is_pr = False
|
||||
self.screen.destinationtype = Destination.Address
|
||||
self.payment_request = None
|
||||
|
||||
def do_paste(self):
|
||||
|
@ -248,10 +269,42 @@ class SendScreen(CScreen):
|
|||
self.app.tx_dialog(tx)
|
||||
return
|
||||
# try to decode as URI/address
|
||||
self.set_URI(data)
|
||||
if data.startswith('ln'):
|
||||
self.set_ln_invoice(data.rstrip())
|
||||
else:
|
||||
self.set_URI(data)
|
||||
|
||||
def _do_send_lightning(self):
|
||||
if not self.screen.amount:
|
||||
self.app.show_error(_('Since the invoice contained no amount, you must enter one'))
|
||||
return
|
||||
invoice = self.screen.address
|
||||
amount_sat = self.app.get_amount(self.screen.amount)
|
||||
try:
|
||||
addr = self.app.wallet.lnworker._check_invoice(invoice, amount_sat)
|
||||
route = self.app.wallet.lnworker._create_route_from_invoice(decoded_invoice=addr)
|
||||
except Exception as e:
|
||||
self.app.show_error(_('Could not find path for payment. Check if you have open channels. Error details:') + ':\n' + repr(e))
|
||||
self.app.network.register_callback(self.payment_completed_async_thread, ['ln_payment_completed'])
|
||||
_addr, _peer, coro = self.app.wallet.lnworker._pay(invoice, amount_sat)
|
||||
fut = asyncio.run_coroutine_threadsafe(coro, self.app.network.asyncio_loop)
|
||||
fut.add_done_callback(self.ln_payment_result)
|
||||
|
||||
def payment_completed_async_thread(self, event, direction, htlc, preimage):
|
||||
Clock.schedule_once(lambda dt: self.payment_completed(direction, htlc, preimage))
|
||||
|
||||
def payment_completed(self, direction, htlc, preimage):
|
||||
self.app.show_info(_('Payment received') if direction == RECEIVED else _('Payment sent'))
|
||||
|
||||
def ln_payment_result(self, fut):
|
||||
if fut.exception():
|
||||
self.app.show_error(_('Lightning payment failed:') + '\n' + repr(fut.exception()))
|
||||
|
||||
def do_send(self):
|
||||
if self.screen.is_pr:
|
||||
if self.screen.destinationtype == Destination.LN:
|
||||
self._do_send_lightning()
|
||||
return
|
||||
elif self.screen.destinationtype == Destination.PR:
|
||||
if self.payment_request.has_expired():
|
||||
self.app.show_error(_('Payment request has expired'))
|
||||
return
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#:import _ electrum.gui.kivy.i18n._
|
||||
#:import Destination electrum.gui.kivy.uix.screens.Destination
|
||||
#:import Decimal decimal.Decimal
|
||||
#:set btc_symbol chr(171)
|
||||
#:set mbtc_symbol chr(187)
|
||||
|
@ -11,7 +12,7 @@ SendScreen:
|
|||
address: ''
|
||||
amount: ''
|
||||
message: ''
|
||||
is_pr: False
|
||||
destinationtype: Destination.Address
|
||||
BoxLayout
|
||||
padding: '12dp', '12dp', '12dp', '12dp'
|
||||
spacing: '12dp'
|
||||
|
@ -36,7 +37,7 @@ SendScreen:
|
|||
on_release: Clock.schedule_once(lambda dt: app.show_info(_('Copy and paste the recipient address using the Paste button, or use the camera to scan a QR code.')))
|
||||
#on_release: Clock.schedule_once(lambda dt: app.popup_dialog('contacts'))
|
||||
CardSeparator:
|
||||
opacity: int(not root.is_pr)
|
||||
opacity: int(root.destinationtype == Destination.Address)
|
||||
color: blue_bottom.foreground_color
|
||||
BoxLayout:
|
||||
size_hint: 1, None
|
||||
|
@ -52,10 +53,10 @@ SendScreen:
|
|||
id: amount_e
|
||||
default_text: _('Amount')
|
||||
text: s.amount if s.amount else _('Amount')
|
||||
disabled: root.is_pr
|
||||
disabled: root.destinationtype == Destination.PR or root.destinationtype == Destination.LN and not s.amount
|
||||
on_release: Clock.schedule_once(lambda dt: app.amount_dialog(s, True))
|
||||
CardSeparator:
|
||||
opacity: int(not root.is_pr)
|
||||
opacity: int(root.destinationtype == Destination.Address)
|
||||
color: blue_bottom.foreground_color
|
||||
BoxLayout:
|
||||
id: message_selection
|
||||
|
@ -69,27 +70,27 @@ SendScreen:
|
|||
pos_hint: {'center_y': .5}
|
||||
BlueButton:
|
||||
id: description
|
||||
text: s.message if s.message else (_('No Description') if root.is_pr else _('Description'))
|
||||
disabled: root.is_pr
|
||||
text: s.message if s.message else ({Destination.LN: _('Lightning invoice contains no description'), Destination.Address: _('Description'), Destination.PR: _('No Description')}[root.destinationtype])
|
||||
disabled: root.destinationtype != Destination.Address
|
||||
on_release: Clock.schedule_once(lambda dt: app.description_dialog(s))
|
||||
CardSeparator:
|
||||
opacity: int(not root.is_pr)
|
||||
opacity: int(root.destinationtype == Destination.Address)
|
||||
color: blue_bottom.foreground_color
|
||||
BoxLayout:
|
||||
size_hint: 1, None
|
||||
height: blue_bottom.item_height
|
||||
height: blue_bottom.item_height if root.destinationtype != Destination.LN else 0
|
||||
spacing: '5dp'
|
||||
Image:
|
||||
source: 'atlas://electrum/gui/kivy/theming/light/star_big_inactive'
|
||||
opacity: 0.7
|
||||
opacity: 0.7 if root.destinationtype != Destination.LN else 0
|
||||
size_hint: None, None
|
||||
size: '22dp', '22dp'
|
||||
pos_hint: {'center_y': .5}
|
||||
BlueButton:
|
||||
id: fee_e
|
||||
default_text: _('Fee')
|
||||
text: app.fee_status
|
||||
on_release: Clock.schedule_once(lambda dt: app.fee_dialog(s, True))
|
||||
text: app.fee_status if root.destinationtype != Destination.LN else ''
|
||||
on_release: Clock.schedule_once(lambda dt: app.fee_dialog(s, True)) if root.destinationtype != Destination.LN else None
|
||||
BoxLayout:
|
||||
size_hint: 1, None
|
||||
height: '48dp'
|
||||
|
|
|
@ -97,6 +97,7 @@ class LNWorker(PrintError):
|
|||
l.append((time.time(), direction, json.loads(encoder.encode(htlc)), bh2u(preimage)))
|
||||
self.wallet.storage.put('lightning_payments_completed', l)
|
||||
self.wallet.storage.write()
|
||||
self.network.trigger_callback('ln_payment_completed', direction, htlc, preimage)
|
||||
|
||||
def list_invoices(self):
|
||||
report = self._list_invoices()
|
||||
|
|
Loading…
Add table
Reference in a new issue