Browse Source

lnurl: make requests async, don't block Qt GUI, rm LUD-16 support

- in lnurl.py, make request methods async
- in Qt GUI, lnurl network requests no longer block the GUI thread
  - but they still do in the kivy GUI
- "lightning address" (LUD-16) support is removed for now as the
  email addresses are indistinguishable from openalias email addresses
  (both protocols should have added and enforced a prefix, or similar,
   to remove this kind of ambiguity -- now we would need to make a
   network request just to identify what kind of ID we were given)
patch-4
SomberNight 3 years ago
parent
commit
ed1567e841
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 22
      electrum/gui/kivy/uix/screens.py
  2. 2
      electrum/gui/qt/completion_text_edit.py
  3. 104
      electrum/gui/qt/main_window.py
  4. 29
      electrum/gui/qt/paytoedit.py
  5. 18
      electrum/lnurl.py
  6. 2
      electrum/network.py

22
electrum/gui/kivy/uix/screens.py

@ -20,8 +20,9 @@ from electrum.transaction import tx_from_any, PartialTxOutput
from electrum.util import (parse_URI, InvalidBitcoinURI, TxMinedInfo, maybe_extract_lightning_payment_identifier, from electrum.util import (parse_URI, InvalidBitcoinURI, TxMinedInfo, maybe_extract_lightning_payment_identifier,
InvoiceError, format_time, parse_max_spend, BITCOIN_BIP21_URI_SCHEME) InvoiceError, format_time, parse_max_spend, BITCOIN_BIP21_URI_SCHEME)
from electrum.lnaddr import lndecode, LnInvoiceException from electrum.lnaddr import lndecode, LnInvoiceException
from electrum.lnurl import decode_lnurl, request_lnurl, callback_lnurl, lightning_address_to_url, LNURLError, LNURL6Data from electrum.lnurl import decode_lnurl, request_lnurl, callback_lnurl, LNURLError, LNURL6Data
from electrum.logging import Logger from electrum.logging import Logger
from electrum.network import Network
from .dialogs.confirm_tx_dialog import ConfirmTxDialog from .dialogs.confirm_tx_dialog import ConfirmTxDialog
@ -178,7 +179,6 @@ class SendScreen(CScreen, Logger):
* lightning-URI (containing bolt11 or lnurl) * lightning-URI (containing bolt11 or lnurl)
* bolt11 invoice * bolt11 invoice
* lnurl * lnurl
* lightning address
Bitcoin identifiers: Bitcoin identifiers:
* bitcoin-URI * bitcoin-URI
* bitcoin address * bitcoin address
@ -191,10 +191,7 @@ class SendScreen(CScreen, Logger):
text = text.strip() text = text.strip()
if not text: if not text:
return return
invoice_or_lnurl = maybe_extract_lightning_payment_identifier(text) if invoice_or_lnurl := maybe_extract_lightning_payment_identifier(text):
if lightning_address_to_url(text):
self.set_lnurl6(text)
elif invoice_or_lnurl:
if invoice_or_lnurl.startswith('lnurl'): if invoice_or_lnurl.startswith('lnurl'):
self.set_lnurl6(invoice_or_lnurl) self.set_lnurl6(invoice_or_lnurl)
else: else:
@ -234,11 +231,10 @@ class SendScreen(CScreen, Logger):
self.is_lightning = True self.is_lightning = True
def set_lnurl6(self, lnurl: str): def set_lnurl6(self, lnurl: str):
url = lightning_address_to_url(lnurl) url = decode_lnurl(lnurl)
if not url:
url = decode_lnurl(lnurl)
domain = urlparse(url).netloc domain = urlparse(url).netloc
lnurl_data = request_lnurl(url, self.app.network.send_http_on_proxy) # FIXME network request blocking GUI thread:
lnurl_data = Network.run_from_another_thread(request_lnurl(url))
if not lnurl_data: if not lnurl_data:
return return
self.lnurl_data = lnurl_data self.lnurl_data = lnurl_data
@ -380,11 +376,11 @@ class SendScreen(CScreen, Logger):
self.app.show_error(f'Amount must be between {self.lnurl_data.min_sendable_sat} and {self.lnurl_data.max_sendable_sat} sat.') self.app.show_error(f'Amount must be between {self.lnurl_data.min_sendable_sat} and {self.lnurl_data.max_sendable_sat} sat.')
return return
try: try:
invoice_data = callback_lnurl( # FIXME network request blocking GUI thread:
invoice_data = Network.run_from_another_thread(callback_lnurl(
self.lnurl_data.callback_url, self.lnurl_data.callback_url,
params={'amount': amount * 1000}, params={'amount': amount * 1000},
request_over_proxy=self.app.network.send_http_on_proxy, ))
)
except LNURLError as e: except LNURLError as e:
self.app.show_error(f"LNURL request encountered error: {e}") self.app.show_error(f"LNURL request encountered error: {e}")
self.do_clear() self.do_clear()

2
electrum/gui/qt/completion_text_edit.py

@ -81,6 +81,8 @@ class CompletionTextEdit(ButtonsTextEdit):
return return
QPlainTextEdit.keyPressEvent(self, e) QPlainTextEdit.keyPressEvent(self, e)
if self.isReadOnly(): # if field became read-only *after* keyPress, exit now
return
ctrlOrShift = bool(e.modifiers() & (Qt.ControlModifier | Qt.ShiftModifier)) ctrlOrShift = bool(e.modifiers() & (Qt.ControlModifier | Qt.ShiftModifier))
if self.completer is None or (ctrlOrShift and not e.text()): if self.completer is None or (ctrlOrShift and not e.text()):

104
electrum/gui/qt/main_window.py

@ -58,7 +58,7 @@ from electrum import (keystore, ecc, constants, util, bitcoin, commands,
from electrum.bitcoin import COIN, is_address from electrum.bitcoin import COIN, is_address
from electrum.plugin import run_hook, BasePlugin from electrum.plugin import run_hook, BasePlugin
from electrum.i18n import _ from electrum.i18n import _
from electrum.util import (format_time, from electrum.util import (format_time, get_asyncio_loop,
UserCancelled, profiler, UserCancelled, profiler,
bh2u, bfh, InvalidPassword, bh2u, bfh, InvalidPassword,
UserFacingException, UserFacingException,
@ -82,7 +82,7 @@ from electrum.simple_config import SimpleConfig
from electrum.logging import Logger from electrum.logging import Logger
from electrum.lnutil import ln_dummy_address, extract_nodeid, ConnStringFormatError from electrum.lnutil import ln_dummy_address, extract_nodeid, ConnStringFormatError
from electrum.lnaddr import lndecode, LnInvoiceException from electrum.lnaddr import lndecode, LnInvoiceException
from electrum.lnurl import decode_lnurl, request_lnurl, callback_lnurl, lightning_address_to_url, LNURLError, LNURL6Data from electrum.lnurl import decode_lnurl, request_lnurl, callback_lnurl, LNURLError, LNURL6Data
from .exception_window import Exception_Hook from .exception_window import Exception_Hook
from .amountedit import AmountEdit, BTCAmountEdit, FreezableLineEdit, FeerateEdit, SizedFreezableLineEdit from .amountedit import AmountEdit, BTCAmountEdit, FreezableLineEdit, FeerateEdit, SizedFreezableLineEdit
@ -201,6 +201,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
payment_request_ok_signal = pyqtSignal() payment_request_ok_signal = pyqtSignal()
payment_request_error_signal = pyqtSignal() payment_request_error_signal = pyqtSignal()
lnurl6_round1_signal = pyqtSignal(object, object)
lnurl6_round2_signal = pyqtSignal(object)
clear_send_tab_signal = pyqtSignal()
#ln_payment_attempt_signal = pyqtSignal(str) #ln_payment_attempt_signal = pyqtSignal(str)
computing_privkeys_signal = pyqtSignal() computing_privkeys_signal = pyqtSignal()
show_privkeys_signal = pyqtSignal() show_privkeys_signal = pyqtSignal()
@ -312,6 +315,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self.payment_request_ok_signal.connect(self.payment_request_ok) self.payment_request_ok_signal.connect(self.payment_request_ok)
self.payment_request_error_signal.connect(self.payment_request_error) self.payment_request_error_signal.connect(self.payment_request_error)
self.lnurl6_round1_signal.connect(self.on_lnurl6_round1)
self.lnurl6_round2_signal.connect(self.on_lnurl6_round2)
self.clear_send_tab_signal.connect(self.do_clear)
self.show_error_signal.connect(self.show_error) self.show_error_signal.connect(self.show_error)
self.history_list.setFocus(True) self.history_list.setFocus(True)
@ -1917,22 +1924,30 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
if not (self._lnurl_data.min_sendable_sat <= amount <= self._lnurl_data.max_sendable_sat): if not (self._lnurl_data.min_sendable_sat <= amount <= self._lnurl_data.max_sendable_sat):
self.show_error(f'Amount must be between {self._lnurl_data.min_sendable_sat} and {self._lnurl_data.max_sendable_sat} sat.') self.show_error(f'Amount must be between {self._lnurl_data.min_sendable_sat} and {self._lnurl_data.max_sendable_sat} sat.')
return return
try:
invoice_data = callback_lnurl( async def f():
self._lnurl_data.callback_url, try:
params={'amount': self.amount_e.get_amount() * 1000}, invoice_data = await callback_lnurl(
request_over_proxy=self.network.send_http_on_proxy, self._lnurl_data.callback_url,
) params={'amount': self.amount_e.get_amount() * 1000},
except LNURLError as e: )
self.show_error(f"LNURL request encountered error: {e}") except LNURLError as e:
self.do_clear() self.show_error_signal.emit(f"LNURL request encountered error: {e}")
return self.clear_send_tab_signal.emit()
invoice = invoice_data.get('pr') return
self.set_bolt11(invoice) invoice = invoice_data.get('pr')
self.lnurl6_round2_signal.emit(invoice)
asyncio.run_coroutine_threadsafe(f(), get_asyncio_loop()) # TODO should be cancellable
self.prepare_for_send_tab_network_lookup()
def on_lnurl6_round2(self, bolt11_invoice: str):
self.set_bolt11(bolt11_invoice)
self.payto_e.setFrozen(True) self.payto_e.setFrozen(True)
self.amount_e.setDisabled(True) self.amount_e.setEnabled(False)
self.fiat_send_e.setDisabled(True) self.fiat_send_e.setEnabled(False)
self.save_button.setEnabled(True) for btn in [self.send_button, self.clear_button, self.save_button]:
btn.setEnabled(True)
self.send_button.restore_original_text() self.send_button.restore_original_text()
self._lnurl_data = None self._lnurl_data = None
@ -2200,14 +2215,15 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self.amount_e.setFrozen(b) self.amount_e.setFrozen(b)
self.max_button.setEnabled(not b) self.max_button.setEnabled(not b)
def prepare_for_payment_request(self): def prepare_for_send_tab_network_lookup(self):
self.show_send_tab() self.show_send_tab()
self.payto_e.is_pr = True self.payto_e.disable_checks = True
for e in [self.payto_e, self.message_e]: for e in [self.payto_e, self.message_e]:
e.setFrozen(True) e.setFrozen(True)
self.lock_amount(True) self.lock_amount(True)
for btn in [self.save_button, self.send_button, self.clear_button]:
btn.setEnabled(False)
self.payto_e.setTextNoCheck(_("please wait...")) self.payto_e.setTextNoCheck(_("please wait..."))
return True
def delete_invoices(self, keys): def delete_invoices(self, keys):
for key in keys: for key in keys:
@ -2224,7 +2240,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self.do_clear() self.do_clear()
self.payment_request = None self.payment_request = None
return return
self.payto_e.is_pr = True self.payto_e.disable_checks = True
if not pr.has_expired(): if not pr.has_expired():
self.payto_e.setGreen() self.payto_e.setGreen()
else: else:
@ -2232,6 +2248,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self.payto_e.setTextNoCheck(pr.get_requestor()) self.payto_e.setTextNoCheck(pr.get_requestor())
self.amount_e.setAmount(pr.get_amount()) self.amount_e.setAmount(pr.get_amount())
self.message_e.setText(pr.get_memo()) self.message_e.setText(pr.get_memo())
self.set_onchain(True)
self.max_button.setEnabled(False)
for btn in [self.send_button, self.clear_button]:
btn.setEnabled(True)
# signal to set fee # signal to set fee
self.amount_e.textEdited.emit("") self.amount_e.textEdited.emit("")
@ -2244,34 +2264,43 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self.do_clear() self.do_clear()
def on_pr(self, request: 'paymentrequest.PaymentRequest'): def on_pr(self, request: 'paymentrequest.PaymentRequest'):
self.set_onchain(True)
self.payment_request = request self.payment_request = request
if self.payment_request.verify(self.contacts): if self.payment_request.verify(self.contacts):
self.payment_request_ok_signal.emit() self.payment_request_ok_signal.emit()
else: else:
self.payment_request_error_signal.emit() self.payment_request_error_signal.emit()
def set_lnurl6_bech32(self, lnurl: str, *, can_use_network: bool = True): def set_lnurl6(self, lnurl: str, *, can_use_network: bool = True):
try: try:
url = decode_lnurl(lnurl) url = decode_lnurl(lnurl)
except LnInvoiceException as e: except LnInvoiceException as e:
self.show_error(_("Error parsing Lightning invoice") + f":\n{e}") self.show_error(_("Error parsing Lightning invoice") + f":\n{e}")
return return
self.set_lnurl6_url(url, can_use_network=can_use_network) if not can_use_network:
def set_lnurl6_url(self, url: str, *, lnurl_data: LNURL6Data = None, can_use_network: bool = True):
domain = urlparse(url).netloc
if lnurl_data is None and can_use_network:
lnurl_data = request_lnurl(url, self.network.send_http_on_proxy)
if not lnurl_data:
return return
async def f():
try:
lnurl_data = await request_lnurl(url)
except LNURLError as e:
self.show_error_signal.emit(f"LNURL request encountered error: {e}")
self.clear_send_tab_signal.emit()
return
self.lnurl6_round1_signal.emit(lnurl_data, url)
asyncio.run_coroutine_threadsafe(f(), get_asyncio_loop()) # TODO should be cancellable
self.prepare_for_send_tab_network_lookup()
def on_lnurl6_round1(self, lnurl_data: LNURL6Data, url: str):
self._lnurl_data = lnurl_data self._lnurl_data = lnurl_data
self.payto_e.setFrozen(True) domain = urlparse(url).netloc
self.payto_e.setTextNoCheck(f"invoice from lnurl") self.payto_e.setTextNoCheck(f"invoice from lnurl")
self.message_e.setText(f"lnurl: {domain}: {lnurl_data.metadata_plaintext}") self.message_e.setText(f"lnurl: {domain}: {lnurl_data.metadata_plaintext}")
self.amount_e.setAmount(lnurl_data.min_sendable_sat) self.amount_e.setAmount(lnurl_data.min_sendable_sat)
self.save_button.setDisabled(True) self.amount_e.setFrozen(False)
self.send_button.setText(_('Get Invoice')) self.send_button.setText(_('Get Invoice'))
for btn in [self.send_button, self.clear_button]:
btn.setEnabled(True)
self.set_onchain(False) self.set_onchain(False)
def set_bolt11(self, invoice: str): def set_bolt11(self, invoice: str):
@ -2314,7 +2343,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
sig = out.get('sig') sig = out.get('sig')
name = out.get('name') name = out.get('name')
if (r or (name and sig)) and can_use_network: if (r or (name and sig)) and can_use_network:
self.prepare_for_payment_request() self.prepare_for_send_tab_network_lookup()
return return
address = out.get('address') address = out.get('address')
amount = out.get('amount') amount = out.get('amount')
@ -2347,14 +2376,13 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
text = text.strip() text = text.strip()
if not text: if not text:
return return
invoice_or_lnurl = maybe_extract_lightning_payment_identifier(text) if invoice_or_lnurl := maybe_extract_lightning_payment_identifier(text):
if invoice_or_lnurl:
if invoice_or_lnurl.startswith('lnurl'): if invoice_or_lnurl.startswith('lnurl'):
self.set_lnurl6_bech32(invoice_or_lnurl, can_use_network=can_use_network) self.set_lnurl6(invoice_or_lnurl, can_use_network=can_use_network)
else: else:
self.set_bolt11(invoice_or_lnurl) self.set_bolt11(invoice_or_lnurl)
elif text.lower().startswith(util.BITCOIN_BIP21_URI_SCHEME + ':'): elif text.lower().startswith(util.BITCOIN_BIP21_URI_SCHEME + ':'):
self.set_bip21(text) self.set_bip21(text, can_use_network=can_use_network)
else: else:
raise ValueError("Could not handle payment identifier.") raise ValueError("Could not handle payment identifier.")
# update fiat amount # update fiat amount
@ -2372,7 +2400,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
for e in [self.message_e, self.amount_e]: for e in [self.message_e, self.amount_e]:
e.setText('') e.setText('')
e.setFrozen(False) e.setFrozen(False)
for e in [self.send_button, self.save_button, self.amount_e, self.fiat_send_e]: for e in [self.send_button, self.save_button, self.clear_button, self.amount_e, self.fiat_send_e]:
e.setEnabled(True) e.setEnabled(True)
self.update_status() self.update_status()
run_hook('do_clear', self) run_hook('do_clear', self)

29
electrum/gui/qt/paytoedit.py

@ -35,7 +35,7 @@ from electrum.util import bfh, parse_max_spend
from electrum.transaction import PartialTxOutput from electrum.transaction import PartialTxOutput
from electrum.bitcoin import opcodes, construct_script from electrum.bitcoin import opcodes, construct_script
from electrum.logging import Logger from electrum.logging import Logger
from electrum.lnurl import LNURLError, lightning_address_to_url, request_lnurl, LNURL6Data from electrum.lnurl import LNURLError
from .qrtextedit import ScanQRTextEdit from .qrtextedit import ScanQRTextEdit
from .completion_text_edit import CompletionTextEdit from .completion_text_edit import CompletionTextEdit
@ -89,7 +89,7 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
self.textChanged.connect(self._on_text_changed) self.textChanged.connect(self._on_text_changed)
self.outputs = [] # type: List[PartialTxOutput] self.outputs = [] # type: List[PartialTxOutput]
self.errors = [] # type: List[PayToLineError] self.errors = [] # type: List[PayToLineError]
self.is_pr = False self.disable_checks = False
self.is_alias = False self.is_alias = False
self.update_size() self.update_size()
self.payto_scriptpubkey = None # type: Optional[bytes] self.payto_scriptpubkey = None # type: Optional[bytes]
@ -107,7 +107,7 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
self.setText(text) self.setText(text)
def do_clear(self): def do_clear(self):
self.is_pr = False self.disable_checks = False
self.is_alias = False self.is_alias = False
self.setText('') self.setText('')
self.setFrozen(False) self.setFrozen(False)
@ -193,7 +193,7 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
if full_check: if full_check:
self.previous_payto = str(self.toPlainText()).strip() self.previous_payto = str(self.toPlainText()).strip()
self.errors = [] self.errors = []
if self.is_pr: if self.disable_checks:
return return
# filter out empty lines # filter out empty lines
lines = [i for i in self.lines() if i] lines = [i for i in self.lines() if i]
@ -229,13 +229,7 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
self.win.set_onchain(True) self.win.set_onchain(True)
self.win.lock_amount(False) self.win.lock_amount(False)
return return
if full_check: # network requests if full_check: # network requests # FIXME blocking GUI thread
# try lightning address lnurl-16 (note: names can collide with openalias, so order matters)
lnurl_data = self._resolve_lightning_address_lnurl16(data)
if lnurl_data:
url = lightning_address_to_url(data)
self.win.set_lnurl6_url(url, lnurl_data=lnurl_data)
return
# try openalias # try openalias
oa_data = self._resolve_openalias(data) oa_data = self._resolve_openalias(data)
if oa_data: if oa_data:
@ -339,6 +333,7 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
def _set_openalias(self, *, key: str, data: dict) -> bool: def _set_openalias(self, *, key: str, data: dict) -> bool:
self.is_alias = True self.is_alias = True
self.setFrozen(True)
key = key.strip() # strip whitespaces key = key.strip() # strip whitespaces
address = data.get('address') address = data.get('address')
name = data.get('name') name = data.get('name')
@ -349,7 +344,6 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
self.win.contacts[key] = ('openalias', name) self.win.contacts[key] = ('openalias', name)
self.win.contact_list.update() self.win.contact_list.update()
self.setFrozen(True)
if data.get('type') == 'openalias': if data.get('type') == 'openalias':
self.validated = data.get('validated') self.validated = data.get('validated')
if self.validated: if self.validated:
@ -359,14 +353,3 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
else: else:
self.validated = None self.validated = None
return True return True
def _resolve_lightning_address_lnurl16(self, text: str) -> Optional[LNURL6Data]:
url = lightning_address_to_url(text)
if not url:
return None
try:
lnurl_data = request_lnurl(url, self.win.network.send_http_on_proxy)
return lnurl_data
except LNURLError as e:
self.logger.info(f"failed to resolve {text} as lnurl16 lightning address. got exc: {e!r}")
return None

18
electrum/lnurl.py

@ -4,7 +4,7 @@
import asyncio import asyncio
import json import json
from typing import Callable, Optional, NamedTuple, Any from typing import Callable, Optional, NamedTuple, Any, TYPE_CHECKING
import re import re
import aiohttp.client_exceptions import aiohttp.client_exceptions
@ -12,6 +12,10 @@ from aiohttp import ClientResponse
from electrum.segwit_addr import bech32_decode, Encoding, convertbits from electrum.segwit_addr import bech32_decode, Encoding, convertbits
from electrum.lnaddr import LnDecodeException from electrum.lnaddr import LnDecodeException
from electrum.network import Network
if TYPE_CHECKING:
from collections.abc import Coroutine
class LNURLError(Exception): class LNURLError(Exception):
@ -44,10 +48,10 @@ class LNURL6Data(NamedTuple):
#tag: str = "payRequest" #tag: str = "payRequest"
def _request_lnurl(url: str, request_over_proxy: Callable) -> dict: async def _request_lnurl(url: str) -> dict:
"""Requests payment data from a lnurl.""" """Requests payment data from a lnurl."""
try: try:
response = request_over_proxy("get", url, timeout=10) response = await Network.async_send_http_on_proxy("get", url, timeout=10)
except asyncio.TimeoutError as e: except asyncio.TimeoutError as e:
raise LNURLError("Server did not reply in time.") from e raise LNURLError("Server did not reply in time.") from e
except aiohttp.client_exceptions.ClientError as e: except aiohttp.client_exceptions.ClientError as e:
@ -62,8 +66,8 @@ def _request_lnurl(url: str, request_over_proxy: Callable) -> dict:
return response return response
def request_lnurl(url: str, request_over_proxy: Callable) -> Optional[LNURL6Data]: async def request_lnurl(url: str) -> Optional[LNURL6Data]:
lnurl_dict = _request_lnurl(url, request_over_proxy) lnurl_dict = await _request_lnurl(url)
tag = lnurl_dict.get('tag') tag = lnurl_dict.get('tag')
if tag != 'payRequest': # only LNURL6 is handled atm if tag != 'payRequest': # only LNURL6 is handled atm
return None return None
@ -81,10 +85,10 @@ def request_lnurl(url: str, request_over_proxy: Callable) -> Optional[LNURL6Data
return data return data
def callback_lnurl(url: str, params: dict, request_over_proxy: Callable) -> dict: async def callback_lnurl(url: str, params: dict) -> dict:
"""Requests an invoice from a lnurl supporting server.""" """Requests an invoice from a lnurl supporting server."""
try: try:
response = request_over_proxy("get", url, params=params) response = await Network.async_send_http_on_proxy("get", url, params=params)
except aiohttp.client_exceptions.ClientError as e: except aiohttp.client_exceptions.ClientError as e:
raise LNURLError(f"Client error: {e}") from e raise LNURLError(f"Client error: {e}") from e
# TODO: handling of specific errors # TODO: handling of specific errors

2
electrum/network.py

@ -1297,7 +1297,7 @@ class Network(Logger, NetworkRetryManager[ServerAddr]):
@classmethod @classmethod
async def async_send_http_on_proxy( async def async_send_http_on_proxy(
cls, method: str, url: str, *, cls, method: str, url: str, *,
params: str = None, params: dict = None,
body: bytes = None, body: bytes = None,
json: dict = None, json: dict = None,
headers=None, headers=None,

Loading…
Cancel
Save