Skip to content

Commit cc1874c

Browse files
authored
Merge pull request #10575 from f321x/lnurlw_prefix
pi: handle lud-17 lnurl URIs
2 parents 8be4f8c + 78135ac commit cc1874c

8 files changed

Lines changed: 58 additions & 7 deletions

File tree

contrib/android/bitcoin_intent.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@
55
<category android:name="android.intent.category.BROWSABLE" />
66
<data android:scheme="bitcoin" />
77
<data android:scheme="lightning" />
8+
<data android:scheme="lnurlw" />
9+
<data android:scheme="lnurlp" />
810
</intent-filter>

contrib/build-wine/electrum.nsi

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ Section
189189
CreateShortCut "$SMPROGRAMS\${PRODUCT_NAME}\${PRODUCT_NAME} Testnet.lnk" "$INSTDIR\electrum-${PRODUCT_VERSION}.exe" "--testnet" "$INSTDIR\electrum-${PRODUCT_VERSION}.exe" 0
190190

191191

192-
;Links bitcoin: and lightning: URIs to Electrum
192+
;Links bitcoin:, lightning: and lnurl LUD-17 URIs to Electrum
193193
WriteRegStr HKCU "Software\Classes\bitcoin" "" "URL:bitcoin Protocol"
194194
WriteRegStr HKCU "Software\Classes\bitcoin" "URL Protocol" ""
195195
WriteRegStr HKCU "Software\Classes\bitcoin" "DefaultIcon" "$\"$INSTDIR\electrum.ico, 0$\""
@@ -198,6 +198,14 @@ Section
198198
WriteRegStr HKCU "Software\Classes\lightning" "URL Protocol" ""
199199
WriteRegStr HKCU "Software\Classes\lightning" "DefaultIcon" "$\"$INSTDIR\electrum.ico, 0$\""
200200
WriteRegStr HKCU "Software\Classes\lightning\shell\open\command" "" "$\"$INSTDIR\electrum-${PRODUCT_VERSION}.exe$\" $\"%1$\""
201+
WriteRegStr HKCU "Software\Classes\lnurlp" "" "URL:lnurlp Protocol"
202+
WriteRegStr HKCU "Software\Classes\lnurlp" "URL Protocol" ""
203+
WriteRegStr HKCU "Software\Classes\lnurlp" "DefaultIcon" "$\"$INSTDIR\electrum.ico, 0$\""
204+
WriteRegStr HKCU "Software\Classes\lnurlp\shell\open\command" "" "$\"$INSTDIR\electrum-${PRODUCT_VERSION}.exe$\" $\"%1$\""
205+
WriteRegStr HKCU "Software\Classes\lnurlw" "" "URL:lnurlw Protocol"
206+
WriteRegStr HKCU "Software\Classes\lnurlw" "URL Protocol" ""
207+
WriteRegStr HKCU "Software\Classes\lnurlw" "DefaultIcon" "$\"$INSTDIR\electrum.ico, 0$\""
208+
WriteRegStr HKCU "Software\Classes\lnurlw\shell\open\command" "" "$\"$INSTDIR\electrum-${PRODUCT_VERSION}.exe$\" $\"%1$\""
201209

202210
;Adds an uninstaller possibility to Windows Uninstall or change a program section
203211
WriteRegStr HKCU "${PRODUCT_UNINST_KEY}" "DisplayName" "$(^Name)"
@@ -229,6 +237,9 @@ Section "Uninstall"
229237
RMDir "$SMPROGRAMS\${PRODUCT_NAME}"
230238

231239
DeleteRegKey HKCU "Software\Classes\bitcoin"
240+
DeleteRegKey HKCU "Software\Classes\lightning"
241+
DeleteRegKey HKCU "Software\Classes\lnurlp"
242+
DeleteRegKey HKCU "Software\Classes\lnurlw"
232243
DeleteRegKey HKCU "Software\${PRODUCT_NAME}"
233244
DeleteRegKey HKCU "${PRODUCT_UNINST_KEY}"
234245
SectionEnd

contrib/osx/pyinstaller.spec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ app = BUNDLE(
135135
'CFBundleURLTypes':
136136
[{
137137
'CFBundleURLName': 'bitcoin',
138-
'CFBundleURLSchemes': ['bitcoin', 'lightning', ],
138+
'CFBundleURLSchemes': ['bitcoin', 'lightning', 'lnurlp', 'lnurlw', ],
139139
}],
140140
'LSMinimumSystemVersion': '11',
141141
'NSCameraUsageDescription': 'Electrum would like to access the camera to scan for QR codes',

electrum.desktop

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ StartupNotify=true
1515
StartupWMClass=electrum
1616
Terminal=false
1717
Type=Application
18-
MimeType=x-scheme-handler/bitcoin;x-scheme-handler/lightning;
18+
MimeType=x-scheme-handler/bitcoin;x-scheme-handler/lightning;x-scheme-handler/lnurlp;x-scheme-handler/lnurlw;
1919
Actions=Testnet;
2020
Keywords=crypto;currency;BTC
2121

electrum/gui/qml/qeapp.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from electrum.plugin import run_hook
2424
from electrum.gui.common_qt.util import get_font_id
2525
from electrum.util import profiler
26+
from electrum.lnurl import SUPPORTED_LNURL_SCHEMES
2627

2728
from .qeconfig import QEConfig
2829
from .qedaemon import QEDaemon
@@ -235,7 +236,9 @@ def on_new_intent(self, intent):
235236
data = str(intent.getDataString())
236237
self.logger.debug(f'received intent: {repr(data)}')
237238
scheme = str(intent.getScheme()).lower()
238-
if scheme == BITCOIN_BIP21_URI_SCHEME or scheme == LIGHTNING_URI_SCHEME:
239+
if scheme == BITCOIN_BIP21_URI_SCHEME \
240+
or scheme == LIGHTNING_URI_SCHEME \
241+
or scheme in SUPPORTED_LNURL_SCHEMES:
239242
self.uriReceived.emit(data)
240243

241244
def startup_finished(self):

electrum/lnurl.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121
_logger = get_logger(__name__)
2222

2323

24+
SUPPORTED_LNURL_SCHEMES = ('lnurlp', 'lnurlw')
25+
26+
2427
class LNURLError(Exception): pass
2528

2629
class UntrustedLNURLError(LNURLError):

electrum/payment_identifier.py

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from .transaction import PartialTxOutput
1717
from .lnurl import (decode_lnurl, request_lnurl, callback_lnurl, LNURLError,
1818
lightning_address_to_url, try_resolve_lnurlpay, LNURL6Data,
19-
LNURL3Data, LNURLData)
19+
LNURL3Data, LNURLData, SUPPORTED_LNURL_SCHEMES)
2020
from .bitcoin import opcodes, construct_script
2121
from .lnaddr import LnInvoiceException
2222
from .lnutil import IncompatibleOrInsaneFeatures
@@ -45,9 +45,25 @@ def remove_uri_prefix(data: str, *, prefix: str) -> str:
4545
return data
4646

4747

48+
def maybe_extract_url_from_lud_17_uri(data: str) -> Optional[str]:
49+
"""https://github.com/lnurl/luds/blob/luds/17.md"""
50+
data = data.strip()
51+
try:
52+
parsed = urllib.parse.urlsplit(data)
53+
except ValueError:
54+
return None
55+
if parsed.scheme not in SUPPORTED_LNURL_SCHEMES:
56+
return None
57+
if not (host := parsed.hostname) or not parsed.path:
58+
return None
59+
is_onion = host.endswith('.onion')
60+
url_scheme = 'http' if is_onion else 'https'
61+
return urllib.parse.urlunsplit(parsed._replace(scheme=url_scheme))
62+
63+
4864
RE_ALIAS = r'(.*?)\s*\<([0-9A-Za-z]{1,})\>'
49-
RE_EMAIL = r'\b[A-Za-z0-9._%+-]+@([A-Za-z0-9-]+\.)+[A-Z|a-z]{2,7}\b'
50-
RE_DOMAIN = r'\b([A-Za-z0-9-]+\.)+[A-Z|a-z]{2,7}\b'
65+
RE_EMAIL = r'\b[A-Za-z0-9._%+-]+@([A-Za-z0-9-]+\.)+[A-Za-z]{2,7}\b'
66+
RE_DOMAIN = r'\b([A-Za-z0-9-]+\.)+[A-Za-z]{2,7}\b'
5167
RE_SCRIPT_FN = r'script\((.*)\)'
5268

5369

@@ -98,6 +114,7 @@ class PaymentIdentifier(Logger):
98114
* openalias
99115
* bip21 URI
100116
* lightning-URI (containing bolt11 or lnurl)
117+
* lnurl-URI (lud17 lnurlw/lnurlp URI)
101118
* bolt11 invoice
102119
* lnurl
103120
* lightning address
@@ -228,6 +245,10 @@ def parse(self, text: str):
228245
self.logger.debug(f'Exception cause {e.args!r}')
229246
return
230247
self.set_state(PaymentIdentifierState.AVAILABLE)
248+
elif lnurl_url := maybe_extract_url_from_lud_17_uri(text):
249+
self._type = PaymentIdentifierType.LNURL
250+
self.lnurl = lnurl_url
251+
self.set_state(PaymentIdentifierState.NEED_RESOLVE)
231252
elif text.lower().startswith(BITCOIN_BIP21_URI_SCHEME + ':'):
232253
try:
233254
out = parse_bip21_URI(text)

tests/test_payment_identifier.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,17 @@ def test_lnurl_basic(self):
184184
self.assertEqual(PaymentIdentifierType.LNURL, pi.type)
185185
self.assertTrue(pi.need_resolve())
186186

187+
# test with lud17 prefix
188+
unsupported_lud_17_lnurl_c = f"lnurlc://service.io/?q=3fc3645b439ce8e7"
189+
pi = PaymentIdentifier(None, unsupported_lud_17_lnurl_c)
190+
self.assertFalse(pi.is_valid())
191+
192+
valid_lud_17_lnurl_w = f"lnurlw://service.io/?q=3fc3645b439ce8e7"
193+
pi = PaymentIdentifier(None, valid_lud_17_lnurl_w)
194+
self.assertTrue(pi.is_valid())
195+
self.assertEqual(PaymentIdentifierType.LNURL, pi.type)
196+
self.assertTrue(pi.need_resolve())
197+
187198
@patch('electrum.payment_identifier.request_lnurl')
188199
def test_lnurl_pay_resolve(self, mock_request_lnurl):
189200
"""Test LNURL-pay (LNURL6) with mocked resolve"""

0 commit comments

Comments
 (0)