add zeroconf handling to qt gui
This commit is contained in:
@@ -98,6 +98,8 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
|
|||||||
self.receive_help_text.setLayout(QHBoxLayout())
|
self.receive_help_text.setLayout(QHBoxLayout())
|
||||||
self.receive_rebalance_button = QPushButton('Rebalance')
|
self.receive_rebalance_button = QPushButton('Rebalance')
|
||||||
self.receive_rebalance_button.suggestion = None
|
self.receive_rebalance_button.suggestion = None
|
||||||
|
self.receive_zeroconf_button = QPushButton(_('Accept'))
|
||||||
|
self.receive_zeroconf_button.clicked.connect(self.on_accept_zeroconf)
|
||||||
|
|
||||||
def on_receive_rebalance():
|
def on_receive_rebalance():
|
||||||
if self.receive_rebalance_button.suggestion:
|
if self.receive_rebalance_button.suggestion:
|
||||||
@@ -115,6 +117,7 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
|
|||||||
buttons = QHBoxLayout()
|
buttons = QHBoxLayout()
|
||||||
buttons.addWidget(self.receive_rebalance_button)
|
buttons.addWidget(self.receive_rebalance_button)
|
||||||
buttons.addWidget(self.receive_swap_button)
|
buttons.addWidget(self.receive_swap_button)
|
||||||
|
buttons.addWidget(self.receive_zeroconf_button)
|
||||||
vbox = QVBoxLayout()
|
vbox = QVBoxLayout()
|
||||||
vbox.addWidget(self.receive_help_text)
|
vbox.addWidget(self.receive_help_text)
|
||||||
vbox.addLayout(buttons)
|
vbox.addLayout(buttons)
|
||||||
@@ -236,26 +239,37 @@ class ReceiveTab(QWidget, MessageBoxMixin, Logger):
|
|||||||
self.ln_help = help_texts.ln_help
|
self.ln_help = help_texts.ln_help
|
||||||
can_rebalance = help_texts.can_rebalance()
|
can_rebalance = help_texts.can_rebalance()
|
||||||
can_swap = help_texts.can_swap()
|
can_swap = help_texts.can_swap()
|
||||||
|
can_zeroconf = help_texts.can_zeroconf()
|
||||||
self.receive_rebalance_button.suggestion = help_texts.ln_rebalance_suggestion
|
self.receive_rebalance_button.suggestion = help_texts.ln_rebalance_suggestion
|
||||||
self.receive_swap_button.suggestion = help_texts.ln_swap_suggestion
|
self.receive_swap_button.suggestion = help_texts.ln_swap_suggestion
|
||||||
self.receive_rebalance_button.setVisible(can_rebalance)
|
self.receive_rebalance_button.setVisible(can_rebalance)
|
||||||
self.receive_swap_button.setVisible(can_swap)
|
self.receive_swap_button.setVisible(can_swap)
|
||||||
self.receive_rebalance_button.setEnabled(can_rebalance and self.window.num_tasks() == 0)
|
self.receive_rebalance_button.setEnabled(can_rebalance and self.window.num_tasks() == 0)
|
||||||
self.receive_swap_button.setEnabled(can_swap and self.window.num_tasks() == 0)
|
self.receive_swap_button.setEnabled(can_swap and self.window.num_tasks() == 0)
|
||||||
|
self.receive_zeroconf_button.setVisible(can_zeroconf)
|
||||||
|
self.receive_zeroconf_button.setEnabled(can_zeroconf)
|
||||||
text, data, help_text, title = self.get_tab_data()
|
text, data, help_text, title = self.get_tab_data()
|
||||||
self.receive_e.setText(text)
|
self.receive_e.setText(text)
|
||||||
self.receive_qr.setData(data)
|
self.receive_qr.setData(data)
|
||||||
self.receive_help_text.setText(help_text)
|
self.receive_help_text.setText(help_text)
|
||||||
for w in [self.receive_e, self.receive_qr]:
|
for w in [self.receive_e, self.receive_qr]:
|
||||||
w.setEnabled(bool(text) and not help_text)
|
w.setEnabled(bool(text) and (not help_text or can_zeroconf))
|
||||||
w.setToolTip(help_text)
|
w.setToolTip(help_text)
|
||||||
# macOS hack (similar to #4777)
|
# macOS hack (similar to #4777)
|
||||||
self.receive_e.repaint()
|
self.receive_e.repaint()
|
||||||
# always show
|
# always show
|
||||||
|
if can_zeroconf:
|
||||||
|
# show the help message if zeroconf so user can first accept it and still sees the invoice
|
||||||
|
# after accepting
|
||||||
|
self.receive_widget.show_help()
|
||||||
self.receive_widget.setVisible(True)
|
self.receive_widget.setVisible(True)
|
||||||
self.toggle_qr_button.setEnabled(True)
|
self.toggle_qr_button.setEnabled(True)
|
||||||
self.update_receive_qr_window()
|
self.update_receive_qr_window()
|
||||||
|
|
||||||
|
def on_accept_zeroconf(self):
|
||||||
|
self.receive_zeroconf_button.setVisible(False)
|
||||||
|
self.update_receive_widgets()
|
||||||
|
|
||||||
def get_tab_data(self):
|
def get_tab_data(self):
|
||||||
if self.URI:
|
if self.URI:
|
||||||
out = self.URI, self.URI, self.URI_help, _('Bitcoin URI')
|
out = self.URI, self.URI, self.URI_help, _('Bitcoin URI')
|
||||||
@@ -374,9 +388,12 @@ class ReceiveWidget(QWidget):
|
|||||||
self.textedit.setVisible(not is_qr)
|
self.textedit.setVisible(not is_qr)
|
||||||
self.qr.setVisible(is_qr)
|
self.qr.setVisible(is_qr)
|
||||||
else:
|
else:
|
||||||
self.help_widget.setVisible(True)
|
self.show_help()
|
||||||
self.textedit.setVisible(False)
|
|
||||||
self.qr.setVisible(False)
|
def show_help(self):
|
||||||
|
self.help_widget.setVisible(True)
|
||||||
|
self.textedit.setVisible(False)
|
||||||
|
self.qr.setVisible(False)
|
||||||
|
|
||||||
def resizeEvent(self, e):
|
def resizeEvent(self, e):
|
||||||
# keep square aspect ratio when resized
|
# keep square aspect ratio when resized
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ from .i18n import _
|
|||||||
from .bip32 import BIP32Node, convert_bip32_intpath_to_strpath, convert_bip32_strpath_to_intpath
|
from .bip32 import BIP32Node, convert_bip32_intpath_to_strpath, convert_bip32_strpath_to_intpath
|
||||||
from .crypto import sha256
|
from .crypto import sha256
|
||||||
from . import util
|
from . import util
|
||||||
|
from .lntransport import extract_nodeid
|
||||||
from .util import (NotEnoughFunds, UserCancelled, profiler, OldTaskGroup, ignore_exceptions,
|
from .util import (NotEnoughFunds, UserCancelled, profiler, OldTaskGroup, ignore_exceptions,
|
||||||
format_satoshis, format_fee_satoshis, NoDynamicFeeEstimates,
|
format_satoshis, format_fee_satoshis, NoDynamicFeeEstimates,
|
||||||
WalletFileException, BitcoinException,
|
WalletFileException, BitcoinException,
|
||||||
@@ -60,6 +61,7 @@ from .util import (NotEnoughFunds, UserCancelled, profiler, OldTaskGroup, ignore
|
|||||||
Fiat, bfh, TxMinedInfo, quantize_feerate, OrderedDictWithIndex)
|
Fiat, bfh, TxMinedInfo, quantize_feerate, OrderedDictWithIndex)
|
||||||
from .simple_config import SimpleConfig
|
from .simple_config import SimpleConfig
|
||||||
from .fee_policy import FeePolicy, FixedFeePolicy, FeeMethod, FEE_RATIO_HIGH_WARNING, FEERATE_WARNING_HIGH_FEE
|
from .fee_policy import FeePolicy, FixedFeePolicy, FeeMethod, FEE_RATIO_HIGH_WARNING, FEERATE_WARNING_HIGH_FEE
|
||||||
|
from .lnutil import MIN_FUNDING_SAT
|
||||||
from .bitcoin import COIN, TYPE_ADDRESS
|
from .bitcoin import COIN, TYPE_ADDRESS
|
||||||
from .bitcoin import is_address, address_to_script, is_minikey, relayfee, dust_threshold
|
from .bitcoin import is_address, address_to_script, is_minikey, relayfee, dust_threshold
|
||||||
from .bitcoin import DummyAddress, DummyAddressUsedInTxException
|
from .bitcoin import DummyAddress, DummyAddressUsedInTxException
|
||||||
@@ -346,6 +348,7 @@ class ReceiveRequestHelp(NamedTuple):
|
|||||||
|
|
||||||
ln_swap_suggestion: Optional[Any] = None
|
ln_swap_suggestion: Optional[Any] = None
|
||||||
ln_rebalance_suggestion: Optional[Any] = None
|
ln_rebalance_suggestion: Optional[Any] = None
|
||||||
|
ln_zeroconf_suggestion: bool = False
|
||||||
|
|
||||||
def can_swap(self) -> bool:
|
def can_swap(self) -> bool:
|
||||||
return bool(self.ln_swap_suggestion)
|
return bool(self.ln_swap_suggestion)
|
||||||
@@ -353,6 +356,9 @@ class ReceiveRequestHelp(NamedTuple):
|
|||||||
def can_rebalance(self) -> bool:
|
def can_rebalance(self) -> bool:
|
||||||
return bool(self.ln_rebalance_suggestion)
|
return bool(self.ln_rebalance_suggestion)
|
||||||
|
|
||||||
|
def can_zeroconf(self) -> bool:
|
||||||
|
return self.ln_zeroconf_suggestion
|
||||||
|
|
||||||
|
|
||||||
class TxWalletDelta(NamedTuple):
|
class TxWalletDelta(NamedTuple):
|
||||||
is_relevant: bool # "related to wallet?"
|
is_relevant: bool # "related to wallet?"
|
||||||
@@ -3250,12 +3256,19 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
|||||||
ln_is_error = False
|
ln_is_error = False
|
||||||
ln_swap_suggestion = None
|
ln_swap_suggestion = None
|
||||||
ln_rebalance_suggestion = None
|
ln_rebalance_suggestion = None
|
||||||
|
ln_zeroconf_suggestion = False
|
||||||
URI = self.get_request_URI(req) or ''
|
URI = self.get_request_URI(req) or ''
|
||||||
lightning_has_channels = (
|
lightning_has_channels = (
|
||||||
self.lnworker and len([chan for chan in self.lnworker.channels.values() if chan.is_open()]) > 0
|
self.lnworker and len([chan for chan in self.lnworker.channels.values() if chan.is_open()]) > 0
|
||||||
)
|
)
|
||||||
lightning_online = self.lnworker and self.lnworker.num_peers() > 0
|
lightning_online = self.lnworker and self.lnworker.num_peers() > 0
|
||||||
can_receive_lightning = self.lnworker and amount_sat <= self.lnworker.num_sats_can_receive()
|
can_receive_lightning = self.lnworker and amount_sat <= self.lnworker.num_sats_can_receive()
|
||||||
|
try:
|
||||||
|
zeroconf_nodeid = extract_nodeid(self.config.ZEROCONF_TRUSTED_NODE)[0]
|
||||||
|
except Exception:
|
||||||
|
zeroconf_nodeid = None
|
||||||
|
can_get_zeroconf_channel = (self.lnworker and self.config.ACCEPT_ZEROCONF_CHANNELS
|
||||||
|
and zeroconf_nodeid in self.lnworker.peers)
|
||||||
status = self.get_invoice_status(req)
|
status = self.get_invoice_status(req)
|
||||||
|
|
||||||
if status == PR_EXPIRED:
|
if status == PR_EXPIRED:
|
||||||
@@ -3281,21 +3294,33 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
|||||||
address_help = URI_help = (_("This address has already been used. "
|
address_help = URI_help = (_("This address has already been used. "
|
||||||
"For better privacy, do not reuse it for new payments."))
|
"For better privacy, do not reuse it for new payments."))
|
||||||
if req.is_lightning():
|
if req.is_lightning():
|
||||||
if not lightning_has_channels:
|
if not lightning_has_channels and not can_get_zeroconf_channel:
|
||||||
ln_is_error = True
|
ln_is_error = True
|
||||||
ln_help = _("You must have an open Lightning channel to receive payments.")
|
ln_help = _("You must have an open Lightning channel to receive payments.")
|
||||||
elif not lightning_online:
|
elif not lightning_online:
|
||||||
ln_is_error = True
|
ln_is_error = True
|
||||||
ln_help = _('You must be online to receive Lightning payments.')
|
ln_help = _('You must be online to receive Lightning payments.')
|
||||||
elif not can_receive_lightning:
|
elif not can_receive_lightning or (amount_sat <= 0 and not lightning_has_channels):
|
||||||
ln_is_error = True
|
|
||||||
ln_rebalance_suggestion = self.lnworker.suggest_rebalance_to_receive(amount_sat)
|
ln_rebalance_suggestion = self.lnworker.suggest_rebalance_to_receive(amount_sat)
|
||||||
ln_swap_suggestion = self.lnworker.suggest_swap_to_receive(amount_sat)
|
ln_swap_suggestion = self.lnworker.suggest_swap_to_receive(amount_sat)
|
||||||
ln_help = _('You do not have the capacity to receive this amount with Lightning.')
|
# prefer to use swaps over JIT channels if possible
|
||||||
if bool(ln_rebalance_suggestion):
|
if can_get_zeroconf_channel and not bool(ln_rebalance_suggestion) and not bool(ln_swap_suggestion):
|
||||||
ln_help += '\n\n' + _('You may have that capacity if you rebalance your channels.')
|
if amount_sat < MIN_FUNDING_SAT:
|
||||||
elif bool(ln_swap_suggestion):
|
ln_is_error = True
|
||||||
ln_help += '\n\n' + _('You may have that capacity if you swap some of your funds.')
|
ln_help = (_('Cannot receive this payment. Request at least {} '
|
||||||
|
'to purchase a Lightning channel from your service provider.')
|
||||||
|
.format(self.config.format_amount_and_units(amount_sat=MIN_FUNDING_SAT)))
|
||||||
|
else:
|
||||||
|
ln_zeroconf_suggestion = True
|
||||||
|
ln_help = _(f'Receiving this payment will purchase a payment channel from your '
|
||||||
|
f'service provider. Service fees are deducted from the incoming payment.')
|
||||||
|
else:
|
||||||
|
ln_is_error = True
|
||||||
|
ln_help = _('You do not have the capacity to receive this amount with Lightning.')
|
||||||
|
if bool(ln_rebalance_suggestion):
|
||||||
|
ln_help += '\n\n' + _('You may have that capacity if you rebalance your channels.')
|
||||||
|
elif bool(ln_swap_suggestion):
|
||||||
|
ln_help += '\n\n' + _('You may have that capacity if you swap some of your funds.')
|
||||||
# for URI that has LN part but no onchain part, copy error:
|
# for URI that has LN part but no onchain part, copy error:
|
||||||
if not addr and ln_is_error:
|
if not addr and ln_is_error:
|
||||||
URI_is_error = ln_is_error
|
URI_is_error = ln_is_error
|
||||||
@@ -3309,6 +3334,7 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
|||||||
ln_is_error=ln_is_error,
|
ln_is_error=ln_is_error,
|
||||||
ln_rebalance_suggestion=ln_rebalance_suggestion,
|
ln_rebalance_suggestion=ln_rebalance_suggestion,
|
||||||
ln_swap_suggestion=ln_swap_suggestion,
|
ln_swap_suggestion=ln_swap_suggestion,
|
||||||
|
ln_zeroconf_suggestion=ln_zeroconf_suggestion
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user