1
0

Merge pull request #9784 from f321x/allow_lnaddress_contacts

qt: allow lightning addresses in contacts
This commit is contained in:
ThomasV
2025-05-15 19:09:48 +02:00
committed by GitHub
5 changed files with 27 additions and 17 deletions

View File

@@ -29,7 +29,7 @@ from dns.exception import DNSException
from . import bitcoin from . import bitcoin
from . import dnssec from . import dnssec
from .util import read_json_file, write_json_file, to_string from .util import read_json_file, write_json_file, to_string, is_valid_email
from .logging import Logger, get_logger from .logging import Logger, get_logger
from .util import trigger_callback from .util import trigger_callback
@@ -83,6 +83,7 @@ class Contacts(dict, Logger):
res = dict.pop(self, key) res = dict.pop(self, key)
self.save() self.save()
return res return res
return None
def resolve(self, k): def resolve(self, k):
if bitcoin.is_address(k): if bitcoin.is_address(k):
@@ -90,11 +91,12 @@ class Contacts(dict, Logger):
'address': k, 'address': k,
'type': 'address' 'type': 'address'
} }
if k in self.keys(): for address, (_type, label) in self.items():
_type, addr = self[k] if k.casefold() != label.casefold():
if _type == 'address': continue
if _type in ('address', 'lnaddress'):
return { return {
'address': addr, 'address': address,
'type': 'contact' 'type': 'contact'
} }
if openalias := self.resolve_openalias(k): if openalias := self.resolve_openalias(k):
@@ -159,6 +161,8 @@ class Contacts(dict, Logger):
if not address: if not address:
continue continue
return address, name, validated return address, name, validated
return None
return None
@staticmethod @staticmethod
def find_regex(haystack, needle): def find_regex(haystack, needle):
@@ -172,11 +176,11 @@ class Contacts(dict, Logger):
for k, v in list(data.items()): for k, v in list(data.items()):
if k == 'contacts': if k == 'contacts':
return self._validate(v) return self._validate(v)
if not bitcoin.is_address(k): if not (bitcoin.is_address(k) or is_valid_email(k)):
data.pop(k) data.pop(k)
else: else:
_type, _ = v _type, _ = v
if _type != 'address': if _type not in ('address', 'lnaddress'):
data.pop(k) data.pop(k)
return data return data

View File

@@ -34,6 +34,7 @@ from electrum.i18n import _
from electrum.bitcoin import is_address from electrum.bitcoin import is_address
from electrum.util import block_explorer_URL from electrum.util import block_explorer_URL
from electrum.plugin import run_hook from electrum.plugin import run_hook
from electrum.gui.qt.util import read_QIcon
from .util import webopen from .util import webopen
from .my_treeview import MyTreeView from .my_treeview import MyTreeView
@@ -118,6 +119,9 @@ class ContactList(MyTreeView):
items[self.Columns.NAME].setEditable(contact_type != 'openalias') items[self.Columns.NAME].setEditable(contact_type != 'openalias')
items[self.Columns.ADDRESS].setEditable(False) items[self.Columns.ADDRESS].setEditable(False)
items[self.Columns.NAME].setData(key, self.ROLE_CONTACT_KEY) items[self.Columns.NAME].setData(key, self.ROLE_CONTACT_KEY)
items[self.Columns.NAME].setIcon(
read_QIcon("lightning" if contact_type == 'lnaddress' else "bitcoin")
)
row_count = self.model().rowCount() row_count = self.model().rowCount()
self.model().insertRow(row_count, items) self.model().insertRow(row_count, items)
if key == current_key: if key == current_key:

View File

@@ -54,8 +54,10 @@ from electrum.bitcoin import COIN, is_address, DummyAddress
from electrum.plugin import run_hook from electrum.plugin import run_hook
from electrum.i18n import _ from electrum.i18n import _
from electrum.util import (format_time, UserCancelled, profiler, bfh, InvalidPassword, from electrum.util import (format_time, UserCancelled, profiler, bfh, InvalidPassword,
UserFacingException, get_new_wallet_name, send_exception_to_crash_reporter, UserFacingException, get_new_wallet_name,
AddTransactionException, os_chmod, UI_UNIT_NAME_TXSIZE_VBYTES, ChoiceItem) send_exception_to_crash_reporter,
AddTransactionException, os_chmod, UI_UNIT_NAME_TXSIZE_VBYTES,
is_valid_email, ChoiceItem)
from electrum.bip21 import BITCOIN_BIP21_URI_SCHEME from electrum.bip21 import BITCOIN_BIP21_URI_SCHEME
from electrum.payment_identifier import PaymentIdentifier from electrum.payment_identifier import PaymentIdentifier
from electrum.invoices import PR_PAID, Invoice from electrum.invoices import PR_PAID, Invoice
@@ -1614,11 +1616,12 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
self.send_tab.payto_contacts(labels) self.send_tab.payto_contacts(labels)
def set_contact(self, label, address): def set_contact(self, label, address):
if not is_address(address): if not (is_address(address) or is_valid_email(address)): # email = lightning address
self.show_error(_('Invalid Address')) self.show_error(_('Invalid Address'))
self.contact_list.update() # Displays original unchanged value self.contact_list.update() # Displays original unchanged value
return False return False
self.contacts[address] = ('address', label) address_type = 'address' if is_address(address) else 'lnaddress'
self.contacts[address] = (address_type, label)
self.contact_list.update() self.contact_list.update()
self.history_list.update() self.history_list.update()
self.update_completions() self.update_completions()
@@ -2008,7 +2011,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
line1.setFixedWidth(32 * char_width_in_lineedit()) line1.setFixedWidth(32 * char_width_in_lineedit())
line2 = QLineEdit() line2 = QLineEdit()
line2.setFixedWidth(32 * char_width_in_lineedit()) line2.setFixedWidth(32 * char_width_in_lineedit())
grid.addWidget(QLabel(_("Address")), 1, 0) address_label = QLabel(_("Address"))
address_label.setToolTip(_("Bitcoin- or Lightning address"))
grid.addWidget(address_label, 1, 0)
grid.addWidget(line1, 1, 1) grid.addWidget(line1, 1, 1)
grid.addWidget(QLabel(_("Name")), 2, 0) grid.addWidget(QLabel(_("Name")), 2, 0)
grid.addWidget(line2, 2, 1) grid.addWidget(line2, 2, 1)

View File

@@ -801,7 +801,7 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
def payto_contacts(self, labels): def payto_contacts(self, labels):
paytos = [self.window.get_contact_payto(label) for label in labels] paytos = [self.window.get_contact_payto(label) for label in labels]
self.window.show_send_tab() self.window.show_send_tab()
self.payto_e.do_clear() self.do_clear()
if len(paytos) == 1: if len(paytos) == 1:
self.logger.debug('payto_e setText 1') self.logger.debug('payto_e setText 1')
self.payto_e.setText(paytos[0]) self.payto_e.setText(paytos[0])

View File

@@ -285,7 +285,7 @@ class PaymentIdentifier(Logger):
'label': contact['name'] 'label': contact['name']
} }
self.set_state(PaymentIdentifierState.AVAILABLE) self.set_state(PaymentIdentifierState.AVAILABLE)
elif contact['type'] == 'openalias': elif contact['type'] in ('openalias', 'lnaddress'):
self._type = PaymentIdentifierType.EMAILLIKE self._type = PaymentIdentifierType.EMAILLIKE
self.emaillike = contact['address'] self.emaillike = contact['address']
self.set_state(PaymentIdentifierState.NEED_RESOLVE) self.set_state(PaymentIdentifierState.NEED_RESOLVE)
@@ -649,9 +649,6 @@ class PaymentIdentifier(Logger):
return pubkey, amount, description return pubkey, amount, description
async def resolve_openalias(self, key: str) -> Optional[dict]: async def resolve_openalias(self, key: str) -> Optional[dict]:
# TODO: below check needed? we already matched RE_EMAIL/RE_DOMAIN
# if not (('.' in key) and ('<' not in key) and (' ' not in key)):
# return None
parts = key.split(sep=',') # assuming single line parts = key.split(sep=',') # assuming single line
if parts and len(parts) > 0 and bitcoin.is_address(parts[0]): if parts and len(parts) > 0 and bitcoin.is_address(parts[0]):
return None return None