Browse Source

add lnurl-pay and lightning address support

* bundles all payment identifiers into handle_payment_identifier
* adds lnurl decoding
* adds lightning address decoding
patch-4
bitromortac 3 years ago
committed by SomberNight
parent
commit
fe2fbbd9b1
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 4
      electrum/gui/kivy/main_window.py
  2. 6
      electrum/gui/kivy/uix/screens.py
  3. 4
      electrum/gui/qt/__init__.py
  4. 101
      electrum/gui/qt/main_window.py
  5. 37
      electrum/gui/qt/paytoedit.py
  6. 75
      electrum/lnurl.py
  7. 12
      electrum/tests/test_lnurl.py
  8. 10
      electrum/util.py
  9. 7
      run_electrum

4
electrum/gui/kivy/main_window.py

@ -18,7 +18,7 @@ from electrum.plugin import run_hook
from electrum import util from electrum import util
from electrum.util import (profiler, InvalidPassword, send_exception_to_crash_reporter, from electrum.util import (profiler, InvalidPassword, send_exception_to_crash_reporter,
format_satoshis, format_satoshis_plain, format_fee_satoshis, format_satoshis, format_satoshis_plain, format_fee_satoshis,
maybe_extract_bolt11_invoice, parse_max_spend) maybe_extract_lightning_payment_identifier, parse_max_spend)
from electrum.util import EventListener, event_listener from electrum.util import EventListener, event_listener
from electrum.invoices import PR_PAID, PR_FAILED, Invoice from electrum.invoices import PR_PAID, PR_FAILED, Invoice
from electrum import blockchain from electrum import blockchain
@ -491,7 +491,7 @@ class ElectrumWindow(App, Logger, EventListener):
if data.lower().startswith('channel_backup:'): if data.lower().startswith('channel_backup:'):
self.import_channel_backup(data) self.import_channel_backup(data)
return return
bolt11_invoice = maybe_extract_bolt11_invoice(data) bolt11_invoice = maybe_extract_lightning_payment_identifier(data)
if bolt11_invoice is not None: if bolt11_invoice is not None:
self.set_ln_invoice(bolt11_invoice) self.set_ln_invoice(bolt11_invoice)
return return

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

@ -16,7 +16,7 @@ from electrum.invoices import (PR_DEFAULT_EXPIRATION_WHEN_CREATING,
pr_expiration_values, Invoice) pr_expiration_values, Invoice)
from electrum import bitcoin, constants from electrum import bitcoin, constants
from electrum.transaction import tx_from_any, PartialTxOutput from electrum.transaction import tx_from_any, PartialTxOutput
from electrum.util import (parse_URI, InvalidBitcoinURI, TxMinedInfo, maybe_extract_bolt11_invoice, from electrum.util import (parse_URI, InvalidBitcoinURI, TxMinedInfo, maybe_extract_lightning_payment_identifier,
InvoiceError, format_time, parse_max_spend) InvoiceError, format_time, parse_max_spend)
from electrum.lnaddr import lndecode, LnInvoiceException from electrum.lnaddr import lndecode, LnInvoiceException
from electrum.logging import Logger from electrum.logging import Logger
@ -172,7 +172,7 @@ class SendScreen(CScreen, Logger):
if not self.app.wallet: if not self.app.wallet:
return return
# interpret as lighting URI # interpret as lighting URI
bolt11_invoice = maybe_extract_bolt11_invoice(text) bolt11_invoice = maybe_extract_lightning_payment_identifier(text)
if bolt11_invoice: if bolt11_invoice:
self.set_ln_invoice(bolt11_invoice) self.set_ln_invoice(bolt11_invoice)
# interpret as BIP21 URI # interpret as BIP21 URI
@ -287,7 +287,7 @@ class SendScreen(CScreen, Logger):
self.app.tx_dialog(tx) self.app.tx_dialog(tx)
return return
# try to decode as URI/address # try to decode as URI/address
bolt11_invoice = maybe_extract_bolt11_invoice(data) bolt11_invoice = maybe_extract_lightning_payment_identifier(data)
if bolt11_invoice is not None: if bolt11_invoice is not None:
self.set_ln_invoice(bolt11_invoice) self.set_ln_invoice(bolt11_invoice)
else: else:

4
electrum/gui/qt/__init__.py

@ -87,7 +87,7 @@ class OpenFileEventFilter(QObject):
def eventFilter(self, obj, event): def eventFilter(self, obj, event):
if event.type() == QtCore.QEvent.FileOpen: if event.type() == QtCore.QEvent.FileOpen:
if len(self.windows) >= 1: if len(self.windows) >= 1:
self.windows[0].pay_to_URI(event.url().toString()) self.windows[0].handle_payment_identifier(event.url().toString())
return True return True
return False return False
@ -383,7 +383,7 @@ class ElectrumGui(BaseElectrumGui, Logger):
self.start_new_window(path, uri=None, force_wizard=True) self.start_new_window(path, uri=None, force_wizard=True)
return return
if uri: if uri:
window.pay_to_URI(uri) window.handle_payment_identifier(uri)
window.bring_to_top() window.bring_to_top()
window.setWindowState(window.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive) window.setWindowState(window.windowState() & ~QtCore.Qt.WindowMinimized | QtCore.Qt.WindowActive)

101
electrum/gui/qt/main_window.py

@ -38,6 +38,7 @@ import queue
import asyncio import asyncio
from typing import Optional, TYPE_CHECKING, Sequence, List, Union, Dict, Set from typing import Optional, TYPE_CHECKING, Sequence, List, Union, Dict, Set
import concurrent.futures import concurrent.futures
from urllib.parse import urlparse
from PyQt5.QtGui import QPixmap, QKeySequence, QIcon, QCursor, QFont from PyQt5.QtGui import QPixmap, QKeySequence, QIcon, QCursor, QFont
from PyQt5.QtCore import Qt, QRect, QStringListModel, QSize, pyqtSignal, QPoint from PyQt5.QtCore import Qt, QRect, QStringListModel, QSize, pyqtSignal, QPoint
@ -62,7 +63,7 @@ from electrum.util import (format_time,
bh2u, bfh, InvalidPassword, bh2u, bfh, InvalidPassword,
UserFacingException, UserFacingException,
get_new_wallet_name, send_exception_to_crash_reporter, get_new_wallet_name, send_exception_to_crash_reporter,
InvalidBitcoinURI, maybe_extract_bolt11_invoice, NotEnoughFunds, InvalidBitcoinURI, maybe_extract_lightning_payment_identifier, NotEnoughFunds,
NoDynamicFeeEstimates, NoDynamicFeeEstimates,
AddTransactionException, BITCOIN_BIP21_URI_SCHEME, AddTransactionException, BITCOIN_BIP21_URI_SCHEME,
InvoiceError, parse_max_spend) InvoiceError, parse_max_spend)
@ -81,6 +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
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
@ -820,7 +822,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
d = self.network.get_donation_address() d = self.network.get_donation_address()
if d: if d:
host = self.network.get_parameters().server.host host = self.network.get_parameters().server.host
self.pay_to_URI('bitcoin:%s?message=donation for %s'%(d, host)) self.handle_payment_identifier('bitcoin:%s?message=donation for %s' % (d, host))
else: else:
self.show_error(_('No donation address for this server')) self.show_error(_('No donation address for this server'))
@ -1573,8 +1575,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
grid.addWidget(self.max_button, 3, 3) grid.addWidget(self.max_button, 3, 3)
self.save_button = EnterButton(_("Save"), self.do_save_invoice) self.save_button = EnterButton(_("Save"), self.do_save_invoice)
self.send_button = EnterButton(_("Pay") + "...", self.do_pay) self.send_button = EnterButton(_("Pay") + "...", self.do_pay_or_get_invoice)
self.clear_button = EnterButton(_("Clear"), self.do_clear) self.clear_button = EnterButton(_("Clear"), self.do_clear)
self._is_lnurl = False
buttons = QHBoxLayout() buttons = QHBoxLayout()
buttons.addStretch(1) buttons.addStretch(1)
@ -1909,7 +1912,31 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self.invoice_list.update() self.invoice_list.update()
self.pending_invoice = None self.pending_invoice = None
def do_pay(self): def do_pay_or_get_invoice(self):
if self._is_lnurl:
amount = self.amount_e.get_amount()
if not (self.lnurl_min_sendable_sat <= amount <= self.lnurl_max_sendable_sat):
self.show_error(f'Amount must be between {self.lnurl_min_sendable_sat} and {self.lnurl_max_sendable_sat} sat.')
return
try:
invoice_data = callback_lnurl(
self.lnurl_callback_url,
params={'amount': self.amount_e.get_amount() * 1000},
request_over_proxy=self.network.send_http_on_proxy,
)
except LNURLError as e:
self.show_error(f"LNURL request encountered error: {e}")
self.do_clear()
return
invoice = invoice_data.get('pr')
self.set_bolt11(invoice)
self.payto_e.setFrozen(True)
self.amount_e.setDisabled(True)
self.fiat_send_e.setDisabled(True)
self.save_button.setEnabled(True)
self.send_button.setText('Pay...')
self._is_lnurl = False
return
self.pending_invoice = self.read_invoice() self.pending_invoice = self.read_invoice()
if not self.pending_invoice: if not self.pending_invoice:
return return
@ -2221,7 +2248,31 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
else: else:
self.payment_request_error_signal.emit() self.payment_request_error_signal.emit()
def set_ln_invoice(self, invoice: str): def set_lnurl6(self, lnurl: str):
url = lightning_address_to_url(lnurl)
if not url:
url = decode_lnurl(lnurl)
domain = urlparse(url).netloc
lnurl_data = request_lnurl(url, self.network.send_http_on_proxy)
self.lnurl_callback_url = lnurl_data.get('callback')
self.lnurl_max_sendable_sat = int(lnurl_data.get('maxSendable')) // 1000
self.lnurl_min_sendable_sat = int(lnurl_data.get('minSendable')) // 1000
metadata = lnurl_data.get('metadata')
tag = lnurl_data.get('tag')
if tag == 'payRequest':
self.payto_e.setFrozen(True)
for m in metadata:
if m[0] == 'text/plain':
self._is_lnurl = True
self.payto_e.setTextNosignal(f"invoice from lnurl")
self.message_e.setText(f"lnurl: {domain}: {m[1]}")
self.amount_e.setAmount(self.lnurl_min_sendable_sat)
self.save_button.setDisabled(True)
self.send_button.setText('Get Invoice')
self.set_onchain(False)
def set_bolt11(self, invoice: str):
"""Parse ln invoice, and prepare the send tab for it.""" """Parse ln invoice, and prepare the send tab for it."""
try: try:
lnaddr = lndecode(invoice) lnaddr = lndecode(invoice)
@ -2237,8 +2288,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
else: else:
description = '' description = ''
self.payto_e.setFrozen(True) self.payto_e.setFrozen(True)
self.payto_e.setText(pubkey) self.payto_e.setTextNosignal(pubkey)
self.payto_e.lightning_invoice = invoice self.payto_e.lightning_invoice = invoice
if not self.message_e.text():
self.message_e.setText(description) self.message_e.setText(description)
if lnaddr.get_amount_sat() is not None: if lnaddr.get_amount_sat() is not None:
self.amount_e.setAmount(lnaddr.get_amount_sat()) self.amount_e.setAmount(lnaddr.get_amount_sat())
@ -2267,7 +2319,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
message = out.get('message') message = out.get('message')
lightning = out.get('lightning') lightning = out.get('lightning')
if lightning: if lightning:
self.set_ln_invoice(lightning) self.handle_payment_identifier(lightning)
return return
# use label as description (not BIP21 compliant) # use label as description (not BIP21 compliant)
if label and not message: if label and not message:
@ -2279,20 +2331,41 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
if amount: if amount:
self.amount_e.setAmount(amount) self.amount_e.setAmount(amount)
def pay_to_URI(self, text: str): def handle_payment_identifier(self, text: str):
"""Takes
Lightning identifiers:
* lightning-URI (containing bolt11 or lnurl)
* bolt11 invoice
* lnurl
* lightning address
Bitcoin identifiers:
* bitcoin-URI
and sets the sending screen.
"""
text = text.strip()
if not text: if not text:
return return
# first interpret as lightning invoice invoice_or_lnurl = maybe_extract_lightning_payment_identifier(text)
bolt11_invoice = maybe_extract_bolt11_invoice(text) if lightning_address_to_url(text):
if bolt11_invoice: self.set_lnurl6(text)
self.set_ln_invoice(bolt11_invoice) elif invoice_or_lnurl:
if invoice_or_lnurl.startswith('lnurl'):
self.set_lnurl6(invoice_or_lnurl)
else: else:
self.set_bolt11(invoice_or_lnurl)
elif text.lower().startswith(util.BITCOIN_BIP21_URI_SCHEME + ':'):
self.set_bip21(text) self.set_bip21(text)
else:
raise ValueError("Could not handle payment identifier.")
# update fiat amount # update fiat amount
self.amount_e.textEdited.emit("") self.amount_e.textEdited.emit("")
self.show_send_tab() self.show_send_tab()
def do_clear(self): def do_clear(self):
self.lnurl_max_sendable_sat = None
self.lnurl_min_sendable_sat = None
self.lnurl_callback_url = None
self._is_lnurl = False
self.max_button.setChecked(False) self.max_button.setChecked(False)
self.payment_request = None self.payment_request = None
self.payto_URI = None self.payto_URI = None
@ -2301,6 +2374,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
for e in [self.payto_e, self.message_e, self.amount_e]: for e in [self.payto_e, 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.payto_e, self.amount_e, self.fiat_send_e]:
e.setEnabled(True)
self.update_status() self.update_status()
run_hook('do_clear', self) run_hook('do_clear', self)
@ -3120,7 +3195,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
return return
# if the user scanned a bitcoin URI # if the user scanned a bitcoin URI
if data.lower().startswith(BITCOIN_BIP21_URI_SCHEME + ':'): if data.lower().startswith(BITCOIN_BIP21_URI_SCHEME + ':'):
self.pay_to_URI(data) self.handle_payment_identifier(data)
return return
if data.lower().startswith('channel_backup:'): if data.lower().startswith('channel_backup:'):
self.import_channel_backup(data) self.import_channel_backup(data)

37
electrum/gui/qt/paytoedit.py

@ -29,13 +29,14 @@ from decimal import Decimal
from typing import NamedTuple, Sequence, Optional, List, TYPE_CHECKING from typing import NamedTuple, Sequence, Optional, List, TYPE_CHECKING
from PyQt5.QtGui import QFontMetrics, QFont from PyQt5.QtGui import QFontMetrics, QFont
from PyQt5.QtCore import QTimer
from electrum import bitcoin from electrum import bitcoin
from electrum.util import bfh, maybe_extract_bolt11_invoice, BITCOIN_BIP21_URI_SCHEME, parse_max_spend 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.lnaddr import LnDecodeException 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
@ -84,7 +85,10 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
self.heightMax = (self.fontSpacing * 10) + self.verticalMargins self.heightMax = (self.fontSpacing * 10) + self.verticalMargins
self.c = None self.c = None
self.textChanged.connect(self.check_text) self.timer = QTimer()
self.timer.setSingleShot(True)
self.textChanged.connect(self.start_timer)
self.timer.timeout.connect(self.check_text)
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.is_pr = False
@ -94,11 +98,23 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
self.lightning_invoice = None self.lightning_invoice = None
self.previous_payto = '' self.previous_payto = ''
def start_timer(self):
# we insert a timer between textChanged and check_text to not immediately
# resolve lightning addresses, but rather to wait until the address is typed out fully
delay_time_msec = 300 # about the average typing time in msec a person types a character
self.logger.info("timer fires")
self.timer.start(delay_time_msec)
def setFrozen(self, b): def setFrozen(self, b):
self.setReadOnly(b) self.setReadOnly(b)
self.setStyleSheet(frozen_style if b else normal_style) self.setStyleSheet(frozen_style if b else normal_style)
self.overlay_widget.setHidden(b) self.overlay_widget.setHidden(b)
def setTextNosignal(self, text: str):
self.blockSignals(True)
self.setText(text)
self.blockSignals(False)
def setGreen(self): def setGreen(self):
self.setStyleSheet(util.ColorScheme.GREEN.as_stylesheet(True)) self.setStyleSheet(util.ColorScheme.GREEN.as_stylesheet(True))
@ -170,14 +186,13 @@ class PayToEdit(CompletionTextEdit, ScanQRTextEdit, Logger):
if len(lines) == 1: if len(lines) == 1:
data = lines[0] data = lines[0]
# try bip21 URI try:
if data.lower().startswith(BITCOIN_BIP21_URI_SCHEME + ':'): self.win.handle_payment_identifier(data)
self.win.pay_to_URI(data) except LNURLError as e:
return self.show_error(e)
# try LN invoice except ValueError:
bolt11_invoice = maybe_extract_bolt11_invoice(data) pass
if bolt11_invoice is not None: else:
self.win.set_ln_invoice(bolt11_invoice)
return return
# try "address, amount" on-chain format # try "address, amount" on-chain format
try: try:

75
electrum/lnurl.py

@ -0,0 +1,75 @@
"""Module for lnurl-related functionality."""
# https://github.com/sipa/bech32/tree/master/ref/python
# https://github.com/lnbits/lnurl
import asyncio
import json
from typing import Callable, Optional
import re
import aiohttp.client_exceptions
from aiohttp import ClientResponse
from electrum.segwit_addr import bech32_decode, Encoding, convertbits
from electrum.lnaddr import LnDecodeException
class LNURLError(Exception):
pass
def decode_lnurl(lnurl: str) -> str:
"""Converts bech32 encoded lnurl to url."""
decoded_bech32 = bech32_decode(
lnurl, ignore_long_length=True
)
hrp = decoded_bech32.hrp
data = decoded_bech32.data
if decoded_bech32.encoding is None:
raise LnDecodeException("Bad bech32 checksum")
if decoded_bech32.encoding != Encoding.BECH32:
raise LnDecodeException("Bad bech32 encoding: must be using vanilla BECH32")
if not hrp.startswith("lnurl"):
raise LnDecodeException("Does not start with lnurl")
data = convertbits(data, 5, 8, False)
url = bytes(data).decode("utf-8")
return url
def request_lnurl(url: str, request_over_proxy: Callable) -> dict:
"""Requests payment data from a lnurl."""
try:
response = request_over_proxy("get", url, timeout=2)
except asyncio.TimeoutError as e:
raise LNURLError("Server did not reply in time.") from e
except aiohttp.client_exceptions.ClientError as e:
raise LNURLError(f"Client error: {e}") from e
# TODO: handling of specific client errors
response = json.loads(response)
if "metadata" in response:
response["metadata"] = json.loads(response["metadata"])
status = response.get("status")
if status and status == "ERROR":
raise LNURLError(f"LNURL request encountered an error: {response['reason']}")
return response
def callback_lnurl(url: str, params: dict, request_over_proxy: Callable) -> dict:
"""Requests an invoice from a lnurl supporting server."""
try:
response = request_over_proxy("get", url, params=params)
except aiohttp.client_exceptions.ClientError as e:
raise LNURLError(f"Client error: {e}") from e
# TODO: handling of specific errors
response = json.loads(response)
status = response.get("status")
if status and status == "ERROR":
raise LNURLError(f"LNURL request encountered an error: {response['reason']}")
return response
def lightning_address_to_url(address: str) -> Optional[str]:
"""Converts an email-type lightning address to a decoded lnurl."""
if re.match(r"[^@]+@[^@]+\.[^@]+", address):
username, domain = address.split("@")
return f"https://{domain}/.well-known/lnurlp/{username}"

12
electrum/tests/test_lnurl.py

@ -0,0 +1,12 @@
from unittest import TestCase
from electrum import lnurl
class TestLnurl(TestCase):
def test_decode(self):
LNURL = (
"LNURL1DP68GURN8GHJ7UM9WFMXJCM99E5K7TELWY7NXENRXVMRGDTZXSENJCM98PJNWXQ96S9"
)
url = lnurl.decode_lnurl(LNURL)
self.assertTrue("https://service.io/?q=3fc3645b439ce8e7", url)

10
electrum/util.py

@ -1065,7 +1065,7 @@ def create_bip21_uri(addr, amount_sat: Optional[int], message: Optional[str],
return str(urllib.parse.urlunparse(p)) return str(urllib.parse.urlunparse(p))
def maybe_extract_bolt11_invoice(data: str) -> Optional[str]: def maybe_extract_lightning_payment_identifier(data: str) -> Optional[str]:
data = data.strip() # whitespaces data = data.strip() # whitespaces
data = data.lower() data = data.lower()
if data.startswith(LIGHTNING_URI_SCHEME + ':ln'): if data.startswith(LIGHTNING_URI_SCHEME + ':ln'):
@ -1076,6 +1076,14 @@ def maybe_extract_bolt11_invoice(data: str) -> Optional[str]:
return None return None
def is_uri(data: str) -> bool:
data = data.lower()
if (data.startswith(LIGHTNING_URI_SCHEME + ":") or
data.startswith(BITCOIN_BIP21_URI_SCHEME + ':')):
return True
return False
# Python bug (http://bugs.python.org/issue1927) causes raw_input # Python bug (http://bugs.python.org/issue1927) causes raw_input
# to be redirected improperly between stdin/stderr on Unix systems # to be redirected improperly between stdin/stderr on Unix systems
#TODO: py3 #TODO: py3

7
run_electrum

@ -98,7 +98,7 @@ from electrum.wallet_db import WalletDB
from electrum.wallet import Wallet from electrum.wallet import Wallet
from electrum.storage import WalletStorage from electrum.storage import WalletStorage
from electrum.util import print_msg, print_stderr, json_encode, json_decode, UserCancelled from electrum.util import print_msg, print_stderr, json_encode, json_decode, UserCancelled
from electrum.util import InvalidPassword, BITCOIN_BIP21_URI_SCHEME, LIGHTNING_URI_SCHEME from electrum.util import InvalidPassword
from electrum.commands import get_parser, known_commands, Commands, config_variables from electrum.commands import get_parser, known_commands, Commands, config_variables
from electrum import daemon from electrum import daemon
from electrum import keystore from electrum import keystore
@ -362,10 +362,7 @@ def main():
# check uri # check uri
uri = config_options.get('url') uri = config_options.get('url')
if uri and not ( if uri and not util.is_uri(uri):
uri.lower().startswith(BITCOIN_BIP21_URI_SCHEME + ':') or
uri.lower().startswith(LIGHTNING_URI_SCHEME + ':')
):
print_stderr('unknown command:', uri) print_stderr('unknown command:', uri)
sys.exit(1) sys.exit(1)

Loading…
Cancel
Save