lnworker: enforce creation of PaymentInfo for b11
Enforce that the information used to create a bolt11 invoice using `get_bolt11_invoice()` is similar to the related instance of PaymentInfo by requiring a PaymentInfo as argument for `get_bolt11_invoice()`. This way the invoice cannot differ from the created PaymentInfo. This allows to use the information in PaymentInfo for validation of incoming htlcs more reliably. To cover all required information for the creation of a b11 invoice the PaymentInfo class has to be extended with a expiry and min_final_cltv_expiry. This requires a db upgrade.
This commit is contained in:
@@ -68,7 +68,7 @@ from .wallet import (
|
||||
)
|
||||
from .address_synchronizer import TX_HEIGHT_LOCAL
|
||||
from .mnemonic import Mnemonic
|
||||
from .lnutil import (channel_id_from_funding_tx, LnFeatures, SENT, MIN_FINAL_CLTV_DELTA_FOR_INVOICE,
|
||||
from .lnutil import (channel_id_from_funding_tx, LnFeatures, SENT, MIN_FINAL_CLTV_DELTA_ACCEPTED,
|
||||
PaymentFeeBudget, NBLOCK_CLTV_DELTA_TOO_FAR_INTO_FUTURE)
|
||||
from .plugin import run_hook, DeviceMgr, Plugins
|
||||
from .version import ELECTRUM_VERSION
|
||||
@@ -1382,7 +1382,7 @@ class Commands(Logger):
|
||||
amount: Optional[Decimal] = None,
|
||||
memo: str = "",
|
||||
expiry: int = 3600,
|
||||
min_final_cltv_expiry_delta: int = MIN_FINAL_CLTV_DELTA_FOR_INVOICE * 2,
|
||||
min_final_cltv_expiry_delta: int = MIN_FINAL_CLTV_DELTA_ACCEPTED * 2,
|
||||
wallet: Abstract_Wallet = None
|
||||
) -> dict:
|
||||
"""
|
||||
@@ -1399,23 +1399,23 @@ class Commands(Logger):
|
||||
assert payment_hash not in wallet.lnworker.payment_info, "Payment hash already used!"
|
||||
assert payment_hash not in wallet.lnworker.dont_settle_htlcs, "Payment hash already used!"
|
||||
assert wallet.lnworker.get_preimage(bfh(payment_hash)) is None, "Already got a preimage for this payment hash!"
|
||||
assert MIN_FINAL_CLTV_DELTA_FOR_INVOICE < min_final_cltv_expiry_delta < 576, "Use a sane min_final_cltv_expiry_delta value"
|
||||
assert MIN_FINAL_CLTV_DELTA_ACCEPTED < min_final_cltv_expiry_delta < 576, "Use a sane min_final_cltv_expiry_delta value"
|
||||
amount = amount if amount and satoshis(amount) > 0 else None # make amount either >0 or None
|
||||
inbound_capacity = wallet.lnworker.num_sats_can_receive()
|
||||
assert inbound_capacity > satoshis(amount or 0), \
|
||||
f"Not enough inbound capacity [{inbound_capacity} sat] to receive this payment"
|
||||
|
||||
lnaddr, invoice = wallet.lnworker.get_bolt11_invoice(
|
||||
payment_hash=bfh(payment_hash),
|
||||
amount_msat=satoshis(amount) * 1000 if amount else None,
|
||||
message=memo,
|
||||
expiry=expiry,
|
||||
min_final_cltv_expiry_delta=min_final_cltv_expiry_delta,
|
||||
fallback_address=None
|
||||
)
|
||||
wallet.lnworker.add_payment_info_for_hold_invoice(
|
||||
bfh(payment_hash),
|
||||
satoshis(amount) if amount else None,
|
||||
lightning_amount_sat=satoshis(amount) if amount else None,
|
||||
min_final_cltv_delta=min_final_cltv_expiry_delta,
|
||||
exp_delay=expiry,
|
||||
)
|
||||
info = wallet.lnworker.get_payment_info(bfh(payment_hash))
|
||||
lnaddr, invoice = wallet.lnworker.get_bolt11_invoice(
|
||||
payment_info=info,
|
||||
message=memo,
|
||||
fallback_address=None
|
||||
)
|
||||
wallet.lnworker.dont_settle_htlcs[payment_hash] = None
|
||||
wallet.set_label(payment_hash, memo)
|
||||
|
||||
@@ -2758,7 +2758,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
self.closing_warning_callbacks.append(warning_callback)
|
||||
|
||||
def _check_ongoing_force_closures(self) -> Optional[str]:
|
||||
from electrum.lnutil import MIN_FINAL_CLTV_DELTA_FOR_INVOICE
|
||||
from electrum.lnutil import MIN_FINAL_CLTV_DELTA_ACCEPTED
|
||||
if not self.wallet.has_lightning():
|
||||
return None
|
||||
if not self.network:
|
||||
@@ -2767,7 +2767,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
if not force_closes:
|
||||
return
|
||||
# fixme: this is inaccurate, we need local_height - cltv_of_htlc
|
||||
cltv_delta = MIN_FINAL_CLTV_DELTA_FOR_INVOICE
|
||||
cltv_delta = MIN_FINAL_CLTV_DELTA_ACCEPTED
|
||||
msg = '\n\n'.join([
|
||||
_("Pending channel force-close"),
|
||||
messages.MSG_FORCE_CLOSE_WARNING.format(cltv_delta),
|
||||
|
||||
@@ -505,9 +505,10 @@ MIN_FUNDING_SAT = 200_000
|
||||
# the minimum cltv_expiry accepted for newly received HTLCs
|
||||
# note: when changing, consider Blockchain.is_tip_stale()
|
||||
MIN_FINAL_CLTV_DELTA_ACCEPTED = 144
|
||||
# set it a tiny bit higher for invoices as blocks could get mined
|
||||
# during forward path of payment
|
||||
MIN_FINAL_CLTV_DELTA_FOR_INVOICE = MIN_FINAL_CLTV_DELTA_ACCEPTED + 3
|
||||
|
||||
# buffer added to min_final_cltv_delta of created bolt11 invoices to make verifying the cltv delta
|
||||
# of incoming payment htlcs reliable even if some blocks have been mined during forwarding
|
||||
MIN_FINAL_CLTV_DELTA_BUFFER_INVOICE = 3
|
||||
|
||||
# the deadline for offered HTLCs:
|
||||
# the deadline after which the channel has to be failed and timed out on-chain
|
||||
|
||||
@@ -65,11 +65,11 @@ from .lnchannel import Channel, AbstractChannel, ChannelState, PeerState, HTLCWi
|
||||
from .lnrater import LNRater
|
||||
from .lnutil import (
|
||||
get_compressed_pubkey_from_bech32, serialize_htlc_key, deserialize_htlc_key, PaymentFailure, generate_keypair,
|
||||
LnKeyFamily, LOCAL, REMOTE, MIN_FINAL_CLTV_DELTA_FOR_INVOICE, SENT, RECEIVED, HTLCOwner, UpdateAddHtlc, LnFeatures,
|
||||
LnKeyFamily, LOCAL, REMOTE, MIN_FINAL_CLTV_DELTA_ACCEPTED, SENT, RECEIVED, HTLCOwner, UpdateAddHtlc, LnFeatures,
|
||||
ShortChannelID, HtlcLog, NoPathFound, InvalidGossipMsg, FeeBudgetExceeded, ImportedChannelBackupStorage,
|
||||
OnchainChannelBackupStorage, ln_compare_features, IncompatibleLightningFeatures, PaymentFeeBudget,
|
||||
NBLOCK_CLTV_DELTA_TOO_FAR_INTO_FUTURE, GossipForwardingMessage, MIN_FUNDING_SAT,
|
||||
RecvMPPResolution, ReceivedMPPStatus,
|
||||
MIN_FINAL_CLTV_DELTA_BUFFER_INVOICE, ReceivedMPPStatus, RecvMPPResolution,
|
||||
)
|
||||
from .lnonion import (
|
||||
decode_onion_error, OnionFailureCode, OnionRoutingFailure, OnionPacket,
|
||||
@@ -119,12 +119,22 @@ class PaymentInfo:
|
||||
amount_msat: Optional[int]
|
||||
direction: int
|
||||
status: int
|
||||
min_final_cltv_delta: int
|
||||
expiry_delay: int
|
||||
creation_ts: int = dataclasses.field(default_factory=lambda: int(time.time()))
|
||||
|
||||
@property
|
||||
def expiration_ts(self):
|
||||
return self.creation_ts + self.expiry_delay
|
||||
|
||||
def validate(self):
|
||||
assert isinstance(self.payment_hash, bytes) and len(self.payment_hash) == 32
|
||||
assert self.amount_msat is None or isinstance(self.amount_msat, int)
|
||||
assert isinstance(self.direction, int)
|
||||
assert isinstance(self.status, int)
|
||||
assert isinstance(self.min_final_cltv_delta, int)
|
||||
assert isinstance(self.expiry_delay, int) and self.expiry_delay > 0
|
||||
assert isinstance(self.creation_ts, int)
|
||||
|
||||
def __post_init__(self):
|
||||
self.validate()
|
||||
@@ -864,7 +874,8 @@ class LNWallet(LNWorker):
|
||||
LNWorker.__init__(self, self.node_keypair, features, config=self.config)
|
||||
self.lnwatcher = LNWatcher(self)
|
||||
self.lnrater: LNRater = None
|
||||
self.payment_info = self.db.get_dict('lightning_payments') # RHASH -> amount, direction, is_paid
|
||||
# lightning_payments: RHASH -> amount_msat, direction, status, min_final_cltv_delta, expiry_delay, creation_ts
|
||||
self.payment_info = self.db.get_dict('lightning_payments') # type: dict[str, Tuple[Optional[int], int, int, int, int, int]]
|
||||
self._preimages = self.db.get_dict('lightning_preimages') # RHASH -> preimage
|
||||
self._bolt11_cache = {}
|
||||
# note: this sweep_address is only used as fallback; as it might result in address-reuse
|
||||
@@ -1567,6 +1578,8 @@ class LNWallet(LNWorker):
|
||||
amount_msat=amount_to_pay,
|
||||
direction=SENT,
|
||||
status=PR_UNPAID,
|
||||
min_final_cltv_delta=min_final_cltv_delta,
|
||||
expiry_delay=LN_EXPIRY_NEVER,
|
||||
)
|
||||
self.save_payment_info(info)
|
||||
self.wallet.set_label(key, lnaddr.get_description())
|
||||
@@ -2238,17 +2251,13 @@ class LNWallet(LNWorker):
|
||||
|
||||
def get_bolt11_invoice(
|
||||
self, *,
|
||||
payment_hash: bytes,
|
||||
amount_msat: Optional[int],
|
||||
payment_info: PaymentInfo,
|
||||
message: str,
|
||||
expiry: int, # expiration of invoice (in seconds, relative)
|
||||
fallback_address: Optional[str],
|
||||
channels: Optional[Sequence[Channel]] = None,
|
||||
min_final_cltv_expiry_delta: Optional[int] = None,
|
||||
) -> Tuple[LnAddr, str]:
|
||||
assert isinstance(payment_hash, bytes), f"expected bytes, but got {type(payment_hash)}"
|
||||
|
||||
pair = self._bolt11_cache.get(payment_hash)
|
||||
amount_msat = payment_info.amount_msat
|
||||
pair = self._bolt11_cache.get(payment_info.payment_hash)
|
||||
if pair:
|
||||
lnaddr, invoice = pair
|
||||
assert lnaddr.get_amount_msat() == amount_msat
|
||||
@@ -2265,19 +2274,16 @@ class LNWallet(LNWorker):
|
||||
if needs_jit:
|
||||
# jit only works with single htlcs, mpp will cause LSP to open channels for each htlc
|
||||
invoice_features &= ~ LnFeatures.BASIC_MPP_OPT & ~ LnFeatures.BASIC_MPP_REQ
|
||||
payment_secret = self.get_payment_secret(payment_hash)
|
||||
payment_secret = self.get_payment_secret(payment_info.payment_hash)
|
||||
amount_btc = amount_msat/Decimal(COIN*1000) if amount_msat else None
|
||||
if expiry == 0:
|
||||
expiry = LN_EXPIRY_NEVER
|
||||
if min_final_cltv_expiry_delta is None:
|
||||
min_final_cltv_expiry_delta = MIN_FINAL_CLTV_DELTA_FOR_INVOICE
|
||||
min_final_cltv_delta = payment_info.min_final_cltv_delta + MIN_FINAL_CLTV_DELTA_BUFFER_INVOICE
|
||||
lnaddr = LnAddr(
|
||||
paymenthash=payment_hash,
|
||||
paymenthash=payment_info.payment_hash,
|
||||
amount=amount_btc,
|
||||
tags=[
|
||||
('d', message),
|
||||
('c', min_final_cltv_expiry_delta),
|
||||
('x', expiry),
|
||||
('c', min_final_cltv_delta),
|
||||
('x', payment_info.expiry_delay),
|
||||
('9', invoice_features),
|
||||
('f', fallback_address),
|
||||
] + routing_hints,
|
||||
@@ -2285,7 +2291,7 @@ class LNWallet(LNWorker):
|
||||
payment_secret=payment_secret)
|
||||
invoice = lnencode(lnaddr, self.node_keypair.privkey)
|
||||
pair = lnaddr, invoice
|
||||
self._bolt11_cache[payment_hash] = pair
|
||||
self._bolt11_cache[payment_info.payment_hash] = pair
|
||||
return pair
|
||||
|
||||
def get_payment_secret(self, payment_hash):
|
||||
@@ -2299,14 +2305,23 @@ class LNWallet(LNWorker):
|
||||
payment_secret = self.get_payment_secret(payment_hash)
|
||||
return payment_hash + payment_secret
|
||||
|
||||
def create_payment_info(self, *, amount_msat: Optional[int], write_to_disk=True) -> bytes:
|
||||
def create_payment_info(
|
||||
self, *,
|
||||
amount_msat: Optional[int],
|
||||
min_final_cltv_delta: Optional[int] = None,
|
||||
exp_delay: int = LN_EXPIRY_NEVER,
|
||||
write_to_disk=True
|
||||
) -> bytes:
|
||||
payment_preimage = os.urandom(32)
|
||||
payment_hash = sha256(payment_preimage)
|
||||
min_final_cltv_delta = min_final_cltv_delta or MIN_FINAL_CLTV_DELTA_ACCEPTED
|
||||
info = PaymentInfo(
|
||||
payment_hash=payment_hash,
|
||||
amount_msat=amount_msat,
|
||||
direction=RECEIVED,
|
||||
status=PR_UNPAID,
|
||||
min_final_cltv_delta=min_final_cltv_delta,
|
||||
expiry_delay=exp_delay
|
||||
)
|
||||
self.save_preimage(payment_hash, payment_preimage, write_to_disk=False)
|
||||
self.save_payment_info(info, write_to_disk=False)
|
||||
@@ -2380,22 +2395,34 @@ class LNWallet(LNWorker):
|
||||
key = payment_hash.hex()
|
||||
with self.lock:
|
||||
if key in self.payment_info:
|
||||
amount_msat, direction, status = self.payment_info[key]
|
||||
stored_tuple = self.payment_info[key]
|
||||
amount_msat, direction, status, min_final_cltv_delta, expiry_delay, creation_ts = stored_tuple
|
||||
return PaymentInfo(
|
||||
payment_hash=payment_hash,
|
||||
amount_msat=amount_msat,
|
||||
direction=direction,
|
||||
status=status,
|
||||
min_final_cltv_delta=min_final_cltv_delta,
|
||||
expiry_delay=expiry_delay,
|
||||
creation_ts=creation_ts,
|
||||
)
|
||||
return None
|
||||
|
||||
def add_payment_info_for_hold_invoice(self, payment_hash: bytes, lightning_amount_sat: Optional[int]):
|
||||
def add_payment_info_for_hold_invoice(
|
||||
self,
|
||||
payment_hash: bytes, *,
|
||||
lightning_amount_sat: Optional[int],
|
||||
min_final_cltv_delta: int,
|
||||
exp_delay: int,
|
||||
):
|
||||
amount = lightning_amount_sat * 1000 if lightning_amount_sat else None
|
||||
info = PaymentInfo(
|
||||
payment_hash=payment_hash,
|
||||
amount_msat=amount,
|
||||
direction=RECEIVED,
|
||||
status=PR_UNPAID,
|
||||
min_final_cltv_delta=min_final_cltv_delta,
|
||||
expiry_delay=exp_delay,
|
||||
)
|
||||
self.save_payment_info(info, write_to_disk=False)
|
||||
|
||||
@@ -2411,9 +2438,12 @@ class LNWallet(LNWorker):
|
||||
if old_info := self.get_payment_info(payment_hash=info.payment_hash):
|
||||
if info == old_info:
|
||||
return # already saved
|
||||
if info.direction == SENT:
|
||||
# allow saving of newer PaymentInfo if it is a sending attempt
|
||||
old_info = dataclasses.replace(old_info, creation_ts=info.creation_ts)
|
||||
if info != dataclasses.replace(old_info, status=info.status):
|
||||
# differs more than in status. let's fail
|
||||
raise Exception("payment_hash already in use")
|
||||
raise Exception(f"payment_hash already in use: {info=} != {old_info=}")
|
||||
key = info.payment_hash.hex()
|
||||
self.payment_info[key] = dataclasses.astuple(info)[1:] # drop the payment hash at index 0
|
||||
if write_to_disk:
|
||||
@@ -3031,12 +3061,14 @@ class LNWallet(LNWorker):
|
||||
raise Exception('Rebalance requires two different channels')
|
||||
if self.uses_trampoline() and chan1.node_id == chan2.node_id:
|
||||
raise Exception('Rebalance requires channels from different trampolines')
|
||||
payment_hash = self.create_payment_info(amount_msat=amount_msat)
|
||||
lnaddr, invoice = self.get_bolt11_invoice(
|
||||
payment_hash=payment_hash,
|
||||
payment_hash = self.create_payment_info(
|
||||
amount_msat=amount_msat,
|
||||
exp_delay=3600,
|
||||
)
|
||||
info = self.get_payment_info(payment_hash)
|
||||
lnaddr, invoice = self.get_bolt11_invoice(
|
||||
payment_info=info,
|
||||
message='rebalance',
|
||||
expiry=3600,
|
||||
fallback_address=None,
|
||||
channels=[chan2],
|
||||
)
|
||||
|
||||
@@ -480,12 +480,11 @@ class NWCServer(Logger, EventListener):
|
||||
address=None
|
||||
)
|
||||
req: Request = self.wallet.get_request(key)
|
||||
info = self.wallet.lnworker.get_payment_info(req.payment_hash)
|
||||
try:
|
||||
lnaddr, b11 = self.wallet.lnworker.get_bolt11_invoice(
|
||||
payment_hash=req.payment_hash,
|
||||
amount_msat=amount_msat,
|
||||
payment_info=info,
|
||||
message=description,
|
||||
expiry=expiry,
|
||||
fallback_address=None
|
||||
)
|
||||
except Exception:
|
||||
@@ -538,11 +537,10 @@ class NWCServer(Logger, EventListener):
|
||||
b11 = invoice.lightning_invoice
|
||||
elif self.wallet.get_request(invoice.rhash):
|
||||
direction = "incoming"
|
||||
info = self.wallet.lnworker.get_payment_info(invoice.payment_hash)
|
||||
_, b11 = self.wallet.lnworker.get_bolt11_invoice(
|
||||
payment_hash=bytes.fromhex(invoice.rhash),
|
||||
amount_msat=invoice.amount_msat,
|
||||
payment_info=info,
|
||||
message=invoice.message,
|
||||
expiry=invoice.exp,
|
||||
fallback_address=None
|
||||
)
|
||||
|
||||
@@ -749,11 +747,10 @@ class NWCServer(Logger, EventListener):
|
||||
request: Optional[Request] = self.wallet.get_request(key)
|
||||
if not request or not request.is_lightning() or not status == PR_PAID:
|
||||
return
|
||||
info = self.wallet.lnworker.get_payment_info(request.payment_hash)
|
||||
_, b11 = self.wallet.lnworker.get_bolt11_invoice(
|
||||
payment_hash=request.payment_hash,
|
||||
amount_msat=request.get_amount_msat(),
|
||||
payment_info=info,
|
||||
message=request.message,
|
||||
expiry=request.exp,
|
||||
fallback_address=None
|
||||
)
|
||||
|
||||
|
||||
@@ -36,7 +36,8 @@ from .util import (
|
||||
run_sync_function_on_asyncio_thread, trigger_callback, NoDynamicFeeEstimates, UserFacingException,
|
||||
)
|
||||
from . import lnutil
|
||||
from .lnutil import hex_to_bytes, REDEEM_AFTER_DOUBLE_SPENT_DELAY, Keypair
|
||||
from .lnutil import (hex_to_bytes, REDEEM_AFTER_DOUBLE_SPENT_DELAY, Keypair,
|
||||
MIN_FINAL_CLTV_DELTA_ACCEPTED)
|
||||
from .lnaddr import lndecode
|
||||
from .json_db import StoredObject, stored_in
|
||||
from . import constants
|
||||
@@ -66,7 +67,6 @@ MAX_LOCKTIME_DELTA = 100
|
||||
MIN_FINAL_CLTV_DELTA_FOR_CLIENT = 3 * 144 # note: put in invoice, but is not enforced by receiver in lnpeer.py
|
||||
assert MIN_LOCKTIME_DELTA <= LOCKTIME_DELTA_REFUND <= MAX_LOCKTIME_DELTA
|
||||
assert MAX_LOCKTIME_DELTA < lnutil.MIN_FINAL_CLTV_DELTA_ACCEPTED
|
||||
assert MAX_LOCKTIME_DELTA < lnutil.MIN_FINAL_CLTV_DELTA_FOR_INVOICE
|
||||
assert MAX_LOCKTIME_DELTA < MIN_FINAL_CLTV_DELTA_FOR_CLIENT
|
||||
|
||||
|
||||
@@ -645,33 +645,38 @@ class SwapManager(Logger):
|
||||
else:
|
||||
invoice_amount_sat = lightning_amount_sat
|
||||
|
||||
# add payment info to lnworker
|
||||
self.lnworker.add_payment_info_for_hold_invoice(
|
||||
payment_hash,
|
||||
lightning_amount_sat=invoice_amount_sat,
|
||||
min_final_cltv_delta=min_final_cltv_expiry_delta or MIN_FINAL_CLTV_DELTA_ACCEPTED,
|
||||
exp_delay=300,
|
||||
)
|
||||
info = self.lnworker.get_payment_info(payment_hash)
|
||||
lnaddr1, invoice = self.lnworker.get_bolt11_invoice(
|
||||
payment_hash=payment_hash,
|
||||
amount_msat=invoice_amount_sat * 1000,
|
||||
payment_info=info,
|
||||
message='Submarine swap',
|
||||
expiry=300,
|
||||
fallback_address=None,
|
||||
channels=channels,
|
||||
min_final_cltv_expiry_delta=min_final_cltv_expiry_delta,
|
||||
)
|
||||
margin_to_get_refund_tx_mined = MIN_LOCKTIME_DELTA
|
||||
if not (locktime + margin_to_get_refund_tx_mined < self.network.get_local_height() + lnaddr1.get_min_final_cltv_delta()):
|
||||
raise Exception(
|
||||
f"onchain locktime ({locktime}+{margin_to_get_refund_tx_mined}) "
|
||||
f"too close to LN-htlc-expiry ({self.network.get_local_height()+lnaddr1.get_min_final_cltv_delta()})")
|
||||
# add payment info to lnworker
|
||||
self.lnworker.add_payment_info_for_hold_invoice(payment_hash, invoice_amount_sat)
|
||||
|
||||
if prepay:
|
||||
prepay_hash = self.lnworker.create_payment_info(amount_msat=prepay_amount_sat*1000)
|
||||
prepay_hash = self.lnworker.create_payment_info(
|
||||
amount_msat=prepay_amount_sat*1000,
|
||||
min_final_cltv_delta=min_final_cltv_expiry_delta or MIN_FINAL_CLTV_DELTA_ACCEPTED,
|
||||
exp_delay=300,
|
||||
)
|
||||
info = self.lnworker.get_payment_info(prepay_hash)
|
||||
lnaddr2, prepay_invoice = self.lnworker.get_bolt11_invoice(
|
||||
payment_hash=prepay_hash,
|
||||
amount_msat=prepay_amount_sat * 1000,
|
||||
payment_info=info,
|
||||
message='Submarine swap prepayment',
|
||||
expiry=300,
|
||||
fallback_address=None,
|
||||
channels=channels,
|
||||
min_final_cltv_expiry_delta=min_final_cltv_expiry_delta,
|
||||
)
|
||||
self.lnworker.bundle_payments([payment_hash, prepay_hash])
|
||||
self._prepayments[prepay_hash] = payment_hash
|
||||
|
||||
@@ -3011,11 +3011,11 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
||||
return ''
|
||||
amount_msat = req.get_amount_msat() or None
|
||||
assert (amount_msat is None or amount_msat > 0), amount_msat
|
||||
info = self.lnworker.get_payment_info(payment_hash)
|
||||
assert info.amount_msat == amount_msat, f"{info.amount_msat=} != {amount_msat=}"
|
||||
lnaddr, invoice = self.lnworker.get_bolt11_invoice(
|
||||
payment_hash=payment_hash,
|
||||
amount_msat=amount_msat,
|
||||
payment_info=info,
|
||||
message=req.message,
|
||||
expiry=req.exp,
|
||||
fallback_address=None)
|
||||
return invoice
|
||||
|
||||
@@ -3031,7 +3031,11 @@ class Abstract_Wallet(ABC, Logger, EventListener):
|
||||
timestamp = int(Request._get_cur_time())
|
||||
if address is None:
|
||||
assert self.has_lightning()
|
||||
payment_hash = self.lnworker.create_payment_info(amount_msat=amount_msat, write_to_disk=False)
|
||||
payment_hash = self.lnworker.create_payment_info(
|
||||
amount_msat=amount_msat,
|
||||
exp_delay=exp_delay,
|
||||
write_to_disk=False,
|
||||
)
|
||||
else:
|
||||
payment_hash = None
|
||||
outputs = [PartialTxOutput.from_address_and_value(address, amount_sat)] if address else []
|
||||
|
||||
@@ -73,7 +73,7 @@ class WalletUnfinished(WalletFileException):
|
||||
# seed_version is now used for the version of the wallet file
|
||||
OLD_SEED_VERSION = 4 # electrum versions < 2.0
|
||||
NEW_SEED_VERSION = 11 # electrum versions >= 2.0
|
||||
FINAL_SEED_VERSION = 60 # electrum >= 2.7 will set this to prevent
|
||||
FINAL_SEED_VERSION = 61 # electrum >= 2.7 will set this to prevent
|
||||
# old versions from overwriting new format
|
||||
|
||||
|
||||
@@ -236,6 +236,7 @@ class WalletDBUpgrader(Logger):
|
||||
self._convert_version_58()
|
||||
self._convert_version_59()
|
||||
self._convert_version_60()
|
||||
self._convert_version_61()
|
||||
self.put('seed_version', FINAL_SEED_VERSION) # just to be sure
|
||||
|
||||
def _convert_wallet_type(self):
|
||||
@@ -1157,6 +1158,18 @@ class WalletDBUpgrader(Logger):
|
||||
cb['multisig_funding_privkey'] = None
|
||||
self.data['seed_version'] = 60
|
||||
|
||||
def _convert_version_61(self):
|
||||
if not self._is_upgrade_method_needed(60, 60):
|
||||
return
|
||||
# adding additional fields to PaymentInfo
|
||||
lightning_payments = self.data.get('lightning_payments', {})
|
||||
expiry_never = 100 * 365 * 24 * 60 * 60
|
||||
migration_time = int(time.time())
|
||||
for rhash, (amount_msat, direction, is_paid) in list(lightning_payments.items()):
|
||||
new = (amount_msat, direction, is_paid, 147, expiry_never, migration_time)
|
||||
lightning_payments[rhash] = new
|
||||
self.data['seed_version'] = 61
|
||||
|
||||
def _convert_imported(self):
|
||||
if not self._is_upgrade_method_needed(0, 13):
|
||||
return
|
||||
|
||||
@@ -41,7 +41,7 @@ from electrum.lnworker import PaymentInfo, RECEIVED
|
||||
from electrum.lnonion import OnionFailureCode, OnionRoutingFailure
|
||||
from electrum.lnutil import UpdateAddHtlc
|
||||
from electrum.lnutil import LOCAL, REMOTE
|
||||
from electrum.invoices import PR_PAID, PR_UNPAID, Invoice
|
||||
from electrum.invoices import PR_PAID, PR_UNPAID, Invoice, LN_EXPIRY_NEVER
|
||||
from electrum.interface import GracefulDisconnect
|
||||
from electrum.simple_config import SimpleConfig
|
||||
from electrum.fee_policy import FeeTimeEstimates, FEE_ETA_TARGETS
|
||||
@@ -563,15 +563,8 @@ class TestPeer(ElectrumTestCase):
|
||||
payment_preimage = os.urandom(32)
|
||||
if payment_hash is None:
|
||||
payment_hash = sha256(payment_preimage)
|
||||
info = PaymentInfo(
|
||||
payment_hash=payment_hash,
|
||||
amount_msat=amount_msat,
|
||||
direction=RECEIVED,
|
||||
status=PR_UNPAID,
|
||||
)
|
||||
if payment_preimage:
|
||||
w2.save_preimage(payment_hash, payment_preimage)
|
||||
w2.save_payment_info(info)
|
||||
if include_routing_hints:
|
||||
routing_hints = w2.calc_routing_hints_for_invoice(amount_msat)
|
||||
else:
|
||||
@@ -584,7 +577,16 @@ class TestPeer(ElectrumTestCase):
|
||||
else:
|
||||
payment_secret = None
|
||||
if min_final_cltv_delta is None:
|
||||
min_final_cltv_delta = lnutil.MIN_FINAL_CLTV_DELTA_FOR_INVOICE
|
||||
min_final_cltv_delta = lnutil.MIN_FINAL_CLTV_DELTA_ACCEPTED
|
||||
info = PaymentInfo(
|
||||
payment_hash=payment_hash,
|
||||
amount_msat=amount_msat,
|
||||
direction=RECEIVED,
|
||||
status=PR_UNPAID,
|
||||
min_final_cltv_delta=min_final_cltv_delta,
|
||||
expiry_delay=LN_EXPIRY_NEVER,
|
||||
)
|
||||
w2.save_payment_info(info)
|
||||
lnaddr1 = LnAddr(
|
||||
paymenthash=payment_hash,
|
||||
amount=amount_btc,
|
||||
|
||||
Reference in New Issue
Block a user