fix: make BIP21 URI scheme network-aware for BitcoinPurple

Adds BIP21_URI_SCHEME to AbstractNet (default 'bitcoin'), overridden
to 'btcp' in BitcoinPurple. All parse/create/scan paths now use
constants.net.BIP21_URI_SCHEME so QR codes with btcp:... URIs are
correctly recognised and generated on the Purple network.
This commit is contained in:
2026-05-07 17:16:15 +02:00
parent ad7d2bd8b3
commit 95439306df
6 changed files with 12 additions and 7 deletions
+4 -3
View File
@@ -5,12 +5,13 @@ from decimal import Decimal
from typing import Optional
from . import bitcoin
from . import constants
from .util import format_satoshis_plain
from .bitcoin import COIN, TOTAL_COIN_SUPPLY_LIMIT_IN_BTC
from .bolt11 import decode_bolt11_invoice, BOLT11DecodeException
# note: when checking against these, use .lower() to support case-insensitivity
BITCOIN_BIP21_URI_SCHEME = 'bitcoin'
BITCOIN_BIP21_URI_SCHEME = 'bitcoin' # kept for backward-compat imports
LIGHTNING_URI_SCHEME = 'lightning'
# note: URI scheme handler registrations are duplicated all over the codebase:
@@ -36,7 +37,7 @@ def parse_bip21_URI(uri: str) -> dict:
return {'address': uri}
u = urllib.parse.urlparse(uri)
if u.scheme.lower() != BITCOIN_BIP21_URI_SCHEME:
if u.scheme.lower() != constants.net.BIP21_URI_SCHEME:
raise InvalidBitcoinURI("Not a bitcoin URI")
address = u.path
@@ -127,7 +128,7 @@ def create_bip21_uri(addr, amount_sat: Optional[int], message: Optional[str],
v = urllib.parse.quote(v)
query.append(f"{k}={v}")
p = urllib.parse.ParseResult(
scheme=BITCOIN_BIP21_URI_SCHEME,
scheme=constants.net.BIP21_URI_SCHEME,
netloc='',
path=addr,
params='',
+2
View File
@@ -83,6 +83,7 @@ class AbstractNet:
COIN_SYMBOL: str = "BTC"
COIN_NAME: str = "Bitcoin"
BIP21_URI_SCHEME: str = "bitcoin"
# PoW difficulty parameters (Bitcoin defaults; override per chain as needed)
DIFFICULTY_ADJUSTMENT_INTERVAL: int = 2016 # blocks per retarget window
@@ -283,6 +284,7 @@ class BitcoinPurple(AbstractNet):
ADDRTYPE_P2SH = 55
SEGWIT_HRP = "btcp"
BOLT11_HRP = SEGWIT_HRP
BIP21_URI_SCHEME = "btcp"
GENESIS = "000003823fbf82ea4906cbe214617ce7a70a5da29c19ecb1d65618bcf04ec015"
DEFAULT_PORTS = {'t': '50001', 's': '50002'}
BLOCK_HEIGHT_FIRST_LIGHTNING_CHANNELS = 0
+2 -1
View File
@@ -16,6 +16,7 @@ except ImportError:
# Note: missing QtMultimedia will lead to errors when using QR scanner on desktop
from PyQt6.QtCore import QObject as QVideoSink
from electrum import constants
from electrum.logging import get_logger
from electrum.qrreader import get_qr_reader
from electrum.i18n import _
@@ -144,7 +145,7 @@ class QEQRImageProvider(QQuickImageProvider):
# (unknown schemes might be found when a colon is in a serialized TX, which
# leads to mangling of the tx, so we check for supported schemes.)
uri = urllib.parse.urlparse(qstr)
if uri.scheme and uri.scheme in ['bitcoin', 'lightning']:
if uri.scheme and uri.scheme in [constants.net.BIP21_URI_SCHEME, 'lightning']:
# urlencode request parameters
query = urllib.parse.parse_qs(uri.query)
query = urllib.parse.urlencode(query, doseq=True, quote_via=urllib.parse.quote)
+1 -1
View File
@@ -1562,7 +1562,7 @@ class Interface(Logger):
return ''
if not isinstance(res, str):
raise RequestCorrupted(f'{res!r} should be a str')
address = res.removeprefix('bitcoin:')
address = res.removeprefix(constants.net.BIP21_URI_SCHEME + ':')
if not bitcoin.is_address(address):
# note: do not hard-fail -- allow server to use future-type
# bitcoin address we do not recognize
+1 -1
View File
@@ -341,7 +341,7 @@ class Request(BaseInvoice):
if lightning_invoice:
extra['lightning'] = lightning_invoice
if not addr and lightning_invoice:
return "bitcoin:?lightning="+lightning_invoice
return f"{constants.net.BIP21_URI_SCHEME}:?lightning=" + lightning_invoice
if not addr and not lightning_invoice:
return None
uri = create_bip21_uri(addr, amount, message, extra_query_params=extra)
+2 -1
View File
@@ -7,6 +7,7 @@ from enum import IntEnum
from typing import NamedTuple, Optional, Callable, List, TYPE_CHECKING, Tuple, Union
from . import bitcoin
from . import constants
from .contacts import AliasNotFoundException
from .i18n import _
from .invoices import Invoice
@@ -249,7 +250,7 @@ class PaymentIdentifier(Logger):
self._type = PaymentIdentifierType.LNURL
self.lnurl = lnurl_url
self.set_state(PaymentIdentifierState.NEED_RESOLVE)
elif text.lower().startswith(BITCOIN_BIP21_URI_SCHEME + ':'):
elif text.lower().startswith(constants.net.BIP21_URI_SCHEME + ':'):
try:
out = parse_bip21_URI(text)
except InvalidBitcoinURI as e: