1
0

handle jit invoices in qml

make min funding amount symbol, change Signal name, change Exception type

change minChannelFunding to QEAmount and make message text variable

qml: improve translatibility of strings

init minchannelfunding value in init method

rebase on master
This commit is contained in:
f321x
2025-03-04 17:02:22 +01:00
parent 838189a586
commit af8d1fb401
4 changed files with 69 additions and 10 deletions

View File

@@ -106,10 +106,42 @@ ElDialog {
}
FlatButton {
Layout.fillWidth: true
enabled: Daemon.currentWallet.isLightning && Daemon.currentWallet.lightningCanReceive.satsInt > amountBtc.textAsSats.satsInt
enabled: Daemon.currentWallet.isLightning && (Daemon.currentWallet.lightningCanReceive.satsInt
> amountBtc.textAsSats.satsInt || Daemon.currentWallet.canGetZeroconfChannel)
text: qsTr('Lightning')
icon.source: '../../icons/lightning.png'
onClicked: { dialog.isLightning = true; doAccept() }
onClicked: {
if (Daemon.currentWallet.lightningCanReceive.satsInt > amountBtc.textAsSats.satsInt) {
// can receive on existing channel
dialog.isLightning = true
doAccept()
} else if (Daemon.currentWallet.canGetZeroconfChannel && amountBtc.textAsSats.satsInt
>= Daemon.currentWallet.minChannelFunding.satsInt) {
// ask for confirmation of zeroconf channel to prevent fee surprise
var confirmdialog = app.messageDialog.createObject(dialog, {
title: qsTr('Confirm just-in-time channel'),
text: [qsTr('Receiving this payment will purchase a Lightning channel from your service provider.'),
qsTr('Fees will be deducted from the payment.'),
qsTr('Do you want to continue?')].join(' '),
yesno: true
})
confirmdialog.accepted.connect(function () {
dialog.isLightning = true
doAccept()
})
confirmdialog.open()
} else {
// show error that amnt > 200k is neccessary to get zeroconf channel
var confirmdialog = app.messageDialog.createObject(dialog, {
title: qsTr("Amount too low"),
text: [qsTr("You don't have channels with enough inbound liquidity to receive this payment."),
qsTr("Request at least %1 to open a channel just-in-time.").arg(
Config.formatSats(Daemon.currentWallet.minChannelFunding.satsInt, true))].join(' ')
})
confirmdialog.open()
}
// can't get zeroconf channel and doesn't have enough inbound liquidity
}
}
}
}

View File

@@ -6,6 +6,7 @@ from PyQt6.QtCore import pyqtProperty, pyqtSignal, pyqtSlot, QObject, QTimer, py
from electrum.logging import get_logger
from electrum.invoices import (PR_UNPAID, PR_EXPIRED, PR_UNKNOWN, PR_PAID, PR_INFLIGHT,
PR_FAILED, PR_ROUTING, PR_UNCONFIRMED, LN_EXPIRY_NEVER)
from electrum.lnutil import MIN_FUNDING_SAT
from .qewallet import QEWallet
from .qetypes import QEAmount
@@ -118,9 +119,13 @@ class QERequestDetails(QObject, QtEventListener):
@pyqtProperty(str, notify=detailsChanged)
def bolt11(self):
can_receive = self._wallet.wallet.lnworker.num_sats_can_receive() if self._wallet.wallet.lnworker else 0
if self._req and can_receive > 0 and (self._req.get_amount_sat() or 0) <= can_receive:
bolt11 = self._wallet.wallet.get_bolt11_invoice(self._req)
wallet = self._wallet.wallet
amount_sat = self._req.get_amount_sat() or 0 if self._req else 0
can_receive = wallet.lnworker.num_sats_can_receive() if wallet.lnworker else 0
will_req_zeroconf = wallet.lnworker.receive_requires_jit_channel(amount_msat=amount_sat*1000)
if self._req and ((can_receive > 0 and amount_sat <= can_receive)
or (will_req_zeroconf and amount_sat >= MIN_FUNDING_SAT)):
bolt11 = wallet.get_bolt11_invoice(self._req)
else:
return ''
# encode lightning invoices as uppercase so QR encoding can use

View File

@@ -15,6 +15,7 @@ from electrum.network import TxBroadcastError, BestEffortRequestFailed
from electrum.transaction import PartialTransaction, Transaction
from electrum.util import InvalidPassword, event_listener, AddTransactionException, get_asyncio_loop, NotEnoughFunds, \
NoDynamicFeeEstimates
from electrum.lnutil import MIN_FUNDING_SAT
from electrum.plugin import run_hook
from electrum.wallet import Multisig_Wallet
from electrum.crypto import pw_decode_with_version_and_mac
@@ -26,6 +27,7 @@ from .qeinvoicelistmodel import QEInvoiceListModel, QERequestListModel
from .qetransactionlistmodel import QETransactionListModel
from .qetypes import QEAmount
from .util import QtEventListener, qt_event_listener
from ...lntransport import extract_nodeid
from ...fee_policy import FeePolicy
if TYPE_CHECKING:
@@ -101,6 +103,7 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
self._frozenbalance = QEAmount()
self._totalbalance = QEAmount()
self._lightningcanreceive = QEAmount()
self._minchannelfunding = QEAmount(amount_sat=int(MIN_FUNDING_SAT))
self._lightningcansend = QEAmount()
self._lightningbalancefrozen = QEAmount()
@@ -459,6 +462,11 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
def canSignMessage(self):
return not isinstance(self.wallet, Multisig_Wallet) and not self.wallet.is_watching_only()
canGetZeroconfChannelChanged = pyqtSignal()
@pyqtProperty(bool, notify=canGetZeroconfChannelChanged)
def canGetZeroconfChannel(self) -> bool:
return self.wallet.lnworker and self.wallet.lnworker.can_get_zeroconf_channel()
@pyqtProperty(QEAmount, notify=balanceChanged)
def frozenBalance(self):
c, u, x = self.wallet.get_frozen_balance()
@@ -506,6 +514,10 @@ class QEWallet(AuthMixin, QObject, QtEventListener):
self._lightningcanreceive.satsInt = int(self.wallet.lnworker.num_sats_can_receive())
return self._lightningcanreceive
@pyqtProperty(QEAmount, notify=dataChanged)
def minChannelFunding(self):
return self._minchannelfunding
@pyqtProperty(int, notify=peersUpdated)
def lightningNumPeers(self):
if self.isLightning:

View File

@@ -2193,7 +2193,7 @@ class LNWallet(LNWorker):
timestamp = int(time.time())
needs_jit: bool = self.receive_requires_jit_channel(amount_msat)
routing_hints = self.calc_routing_hints_for_invoice(amount_msat, channels=channels, needs_jit=needs_jit)
self.logger.info(f"creating bolt11 invoice with routing_hints: {routing_hints}, jit: {needs_jit}")
self.logger.info(f"creating bolt11 invoice with routing_hints: {routing_hints}, jit: {needs_jit}, sat: {amount_msat or 0 // 1000}")
invoice_features = self.features.for_invoice()
if not self.uses_trampoline():
invoice_features &= ~ LnFeatures.OPTION_TRAMPOLINE_ROUTING_OPT_ELECTRUM
@@ -2756,10 +2756,8 @@ class LNWallet(LNWorker):
"""Returns true if we cannot receive the amount and have set up a trusted LSP node.
Cannot work reliably with 0 amount invoices as we don't know if we are able to receive it.
"""
# a trusted zeroconf node is configured
if (self.config.ZEROCONF_TRUSTED_NODE
# the zeroconf node is a peer, it doesn't make sense to request a channel from an offline LSP
and extract_nodeid(self.config.ZEROCONF_TRUSTED_NODE)[0] in self.peers
# zeroconf provider is configured and connected
if (self.can_get_zeroconf_channel()
# we cannot receive the amount specified
and ((amount_msat and self.num_sats_can_receive() < (amount_msat // 1000))
# or we cannot receive anything, and it's a 0 amount invoice
@@ -2767,6 +2765,18 @@ class LNWallet(LNWorker):
return True
return False
def can_get_zeroconf_channel(self) -> bool:
if not self.config.ACCEPT_ZEROCONF_CHANNELS and self.config.ZEROCONF_TRUSTED_NODE:
# check if zeroconf is accepted and client has trusted zeroconf node configured
return False
try:
node_id = extract_nodeid(self.wallet.config.ZEROCONF_TRUSTED_NODE)[0]
except ConnStringFormatError:
# invalid connection string
return False
# only return True if we are connected to the zeroconf provider
return node_id in self.peers
def _suggest_channels_for_rebalance(self, direction, amount_sat) -> Sequence[Tuple[Channel, int]]:
"""
Suggest a channel and amount to send/receive with that channel, so that we will be able to receive/send amount_sat