renames: use consistent naming of cltv delta vs cltv abs
to avoid confusing relative vs absolute cltvs
(see b0401a6386)
This commit is contained in:
@@ -103,7 +103,7 @@ class ChannelInfo(NamedTuple):
|
||||
|
||||
class Policy(NamedTuple):
|
||||
key: bytes
|
||||
cltv_expiry_delta: int
|
||||
cltv_delta: int
|
||||
htlc_minimum_msat: int
|
||||
htlc_maximum_msat: Optional[int]
|
||||
fee_base_msat: int
|
||||
@@ -116,7 +116,7 @@ class Policy(NamedTuple):
|
||||
def from_msg(payload: dict) -> 'Policy':
|
||||
return Policy(
|
||||
key = payload['short_channel_id'] + payload['start_node'],
|
||||
cltv_expiry_delta = payload['cltv_expiry_delta'],
|
||||
cltv_delta = payload['cltv_expiry_delta'],
|
||||
htlc_minimum_msat = payload['htlc_minimum_msat'],
|
||||
htlc_maximum_msat = payload.get('htlc_maximum_msat', None),
|
||||
fee_base_msat = payload['fee_base_msat'],
|
||||
@@ -136,7 +136,7 @@ class Policy(NamedTuple):
|
||||
def from_route_edge(route_edge: 'RouteEdge') -> 'Policy':
|
||||
return Policy(
|
||||
key=route_edge.short_channel_id + route_edge.start_node,
|
||||
cltv_expiry_delta=route_edge.cltv_expiry_delta,
|
||||
cltv_delta=route_edge.cltv_delta,
|
||||
htlc_minimum_msat=0,
|
||||
htlc_maximum_msat=None,
|
||||
fee_base_msat=route_edge.fee_base_msat,
|
||||
@@ -441,10 +441,10 @@ class ChannelDB(SqlDB):
|
||||
|
||||
def policy_changed(self, old_policy: Policy, new_policy: Policy, verbose: bool) -> bool:
|
||||
changed = False
|
||||
if old_policy.cltv_expiry_delta != new_policy.cltv_expiry_delta:
|
||||
if old_policy.cltv_delta != new_policy.cltv_delta:
|
||||
changed |= True
|
||||
if verbose:
|
||||
self.logger.info(f'cltv_expiry_delta: {old_policy.cltv_expiry_delta} -> {new_policy.cltv_expiry_delta}')
|
||||
self.logger.info(f'cltv_expiry_delta: {old_policy.cltv_delta} -> {new_policy.cltv_delta}')
|
||||
if old_policy.htlc_minimum_msat != new_policy.htlc_minimum_msat:
|
||||
changed |= True
|
||||
if verbose:
|
||||
|
||||
@@ -85,7 +85,7 @@ class ChannelDetailsDialog(QtWidgets.QDialog, MessageBoxMixin, QtEventListener):
|
||||
def make_htlc_item(self, i: UpdateAddHtlc, direction: Direction) -> HTLCItem:
|
||||
it = HTLCItem(_('Sent HTLC with ID {}' if Direction.SENT == direction else 'Received HTLC with ID {}').format(i.htlc_id))
|
||||
it.appendRow([HTLCItem(_('Amount')),HTLCItem(self.format_msat(i.amount_msat))])
|
||||
it.appendRow([HTLCItem(_('CLTV expiry')),HTLCItem(str(i.cltv_expiry))])
|
||||
it.appendRow([HTLCItem(_('CLTV expiry')), HTLCItem(str(i.cltv_abs))])
|
||||
it.appendRow([HTLCItem(_('Payment hash')),HTLCItem(i.payment_hash.hex())])
|
||||
return it
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ from PyQt5.QtWidgets import (QPushButton, QLabel, QMessageBox, QHBoxLayout,
|
||||
|
||||
from electrum.i18n import _, languages
|
||||
from electrum.util import FileImportFailed, FileExportFailed, make_aiohttp_session, resource_path
|
||||
from electrum.util import EventListener, event_listener
|
||||
from electrum.util import EventListener, event_listener, get_logger
|
||||
from electrum.invoices import PR_UNPAID, PR_PAID, PR_EXPIRED, PR_INFLIGHT, PR_UNKNOWN, PR_FAILED, PR_ROUTING, PR_UNCONFIRMED, PR_BROADCASTING, PR_BROADCAST
|
||||
from electrum.logging import Logger
|
||||
from electrum.qrreader import MissingQrDetectionLib
|
||||
@@ -52,6 +52,8 @@ else:
|
||||
MONOSPACE_FONT = 'monospace'
|
||||
|
||||
|
||||
_logger = get_logger(__name__)
|
||||
|
||||
dialogs = []
|
||||
|
||||
pr_icons = {
|
||||
|
||||
+2
-2
@@ -336,7 +336,7 @@ class LnAddr(object):
|
||||
", ".join([k + '=' + str(v) for k, v in self.tags])
|
||||
)
|
||||
|
||||
def get_min_final_cltv_expiry(self) -> int:
|
||||
def get_min_final_cltv_delta(self) -> int:
|
||||
cltv = self.get_tag('c')
|
||||
if cltv is None:
|
||||
return 18
|
||||
@@ -375,7 +375,7 @@ class LnAddr(object):
|
||||
'description': self.get_description(),
|
||||
'exp': self.get_expiry(),
|
||||
'time': self.date,
|
||||
'min_final_cltv_expiry': self.get_min_final_cltv_expiry(),
|
||||
'min_final_cltv_delta': self.get_min_final_cltv_delta(),
|
||||
'features': self.get_features().get_names(),
|
||||
'tags': self.tags,
|
||||
'unknown_tags': self.unknown_tags,
|
||||
|
||||
@@ -631,7 +631,7 @@ class Channel(AbstractChannel):
|
||||
# TODO enforce this ^
|
||||
|
||||
# our forwarding parameters for forwarding HTLCs through this channel
|
||||
forwarding_cltv_expiry_delta = 144
|
||||
forwarding_cltv_delta = 144
|
||||
forwarding_fee_base_msat = 1000
|
||||
forwarding_fee_proportional_millionths = 1
|
||||
|
||||
@@ -784,7 +784,7 @@ class Channel(AbstractChannel):
|
||||
short_channel_id=scid,
|
||||
channel_flags=channel_flags,
|
||||
message_flags=b'\x01',
|
||||
cltv_expiry_delta=self.forwarding_cltv_expiry_delta,
|
||||
cltv_expiry_delta=self.forwarding_cltv_delta,
|
||||
htlc_minimum_msat=self.config[REMOTE].htlc_minimum_msat,
|
||||
htlc_maximum_msat=htlc_maximum_msat,
|
||||
fee_base_msat=self.forwarding_fee_base_msat,
|
||||
@@ -1549,7 +1549,7 @@ class Channel(AbstractChannel):
|
||||
remote_htlc_pubkey=other_htlc_pubkey,
|
||||
local_htlc_pubkey=this_htlc_pubkey,
|
||||
payment_hash=htlc.payment_hash,
|
||||
cltv_expiry=htlc.cltv_expiry), htlc))
|
||||
cltv_abs=htlc.cltv_abs), htlc))
|
||||
# note: maybe flip initiator here for fee purposes, we want LOCAL and REMOTE
|
||||
# in the resulting dict to correspond to the to_local and to_remote *outputs* of the ctx
|
||||
onchain_fees = calc_fees_for_commitment_tx(
|
||||
@@ -1656,24 +1656,24 @@ class Channel(AbstractChannel):
|
||||
# If there is a received HTLC for which we already released the preimage
|
||||
# but the remote did not revoke yet, and the CLTV of this HTLC is dangerously close
|
||||
# to the present, then unilaterally close channel
|
||||
recv_htlc_deadline = lnutil.NBLOCK_DEADLINE_BEFORE_EXPIRY_FOR_RECEIVED_HTLCS
|
||||
recv_htlc_deadline_delta = lnutil.NBLOCK_DEADLINE_DELTA_BEFORE_EXPIRY_FOR_RECEIVED_HTLCS
|
||||
for sub, dir, ctn in ((LOCAL, RECEIVED, self.get_latest_ctn(LOCAL)),
|
||||
(REMOTE, SENT, self.get_oldest_unrevoked_ctn(REMOTE)),
|
||||
(REMOTE, SENT, self.get_latest_ctn(REMOTE)),):
|
||||
for htlc_id, htlc in self.hm.htlcs_by_direction(subject=sub, direction=dir, ctn=ctn).items():
|
||||
if not self.hm.was_htlc_preimage_released(htlc_id=htlc_id, htlc_proposer=REMOTE):
|
||||
continue
|
||||
if htlc.cltv_expiry - recv_htlc_deadline > local_height:
|
||||
if htlc.cltv_abs - recv_htlc_deadline_delta > local_height:
|
||||
continue
|
||||
htlcs_we_could_reclaim[(RECEIVED, htlc_id)] = htlc
|
||||
# If there is an offered HTLC which has already expired (+ some grace period after), we
|
||||
# will unilaterally close the channel and time out the HTLC
|
||||
offered_htlc_deadline = lnutil.NBLOCK_DEADLINE_AFTER_EXPIRY_FOR_OFFERED_HTLCS
|
||||
offered_htlc_deadline_delta = lnutil.NBLOCK_DEADLINE_DELTA_AFTER_EXPIRY_FOR_OFFERED_HTLCS
|
||||
for sub, dir, ctn in ((LOCAL, SENT, self.get_latest_ctn(LOCAL)),
|
||||
(REMOTE, RECEIVED, self.get_oldest_unrevoked_ctn(REMOTE)),
|
||||
(REMOTE, RECEIVED, self.get_latest_ctn(REMOTE)),):
|
||||
for htlc_id, htlc in self.hm.htlcs_by_direction(subject=sub, direction=dir, ctn=ctn).items():
|
||||
if htlc.cltv_expiry + offered_htlc_deadline > local_height:
|
||||
if htlc.cltv_abs + offered_htlc_deadline_delta > local_height:
|
||||
continue
|
||||
htlcs_we_could_reclaim[(SENT, htlc_id)] = htlc
|
||||
|
||||
|
||||
+10
-10
@@ -212,23 +212,23 @@ def new_onion_packet(
|
||||
|
||||
def calc_hops_data_for_payment(
|
||||
route: 'LNPaymentRoute',
|
||||
amount_msat: int,
|
||||
final_cltv: int, *,
|
||||
amount_msat: int, # that final recipient receives
|
||||
*,
|
||||
final_cltv_abs: int,
|
||||
total_msat: int,
|
||||
payment_secret: bytes,
|
||||
) -> Tuple[List[OnionHopsDataSingle], int, int]:
|
||||
|
||||
"""Returns the hops_data to be used for constructing an onion packet,
|
||||
and the amount_msat and cltv to be used on our immediate channel.
|
||||
and the amount_msat and cltv_abs to be used on our immediate channel.
|
||||
"""
|
||||
if len(route) > NUM_MAX_EDGES_IN_PAYMENT_PATH:
|
||||
raise PaymentFailure(f"too long route ({len(route)} edges)")
|
||||
# payload that will be seen by the last hop:
|
||||
amt = amount_msat
|
||||
cltv = final_cltv
|
||||
cltv_abs = final_cltv_abs
|
||||
hop_payload = {
|
||||
"amt_to_forward": {"amt_to_forward": amt},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": cltv},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": cltv_abs},
|
||||
}
|
||||
# for multipart payments we need to tell the receiver about the total and
|
||||
# partial amounts
|
||||
@@ -244,19 +244,19 @@ def calc_hops_data_for_payment(
|
||||
is_trampoline = route_edge.is_trampoline()
|
||||
if is_trampoline:
|
||||
amt += route_edge.fee_for_edge(amt)
|
||||
cltv += route_edge.cltv_expiry_delta
|
||||
cltv_abs += route_edge.cltv_delta
|
||||
hop_payload = {
|
||||
"amt_to_forward": {"amt_to_forward": amt},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": cltv},
|
||||
"outgoing_cltv_value": {"outgoing_cltv_value": cltv_abs},
|
||||
"short_channel_id": {"short_channel_id": route_edge.short_channel_id},
|
||||
}
|
||||
hops_data.append(
|
||||
OnionHopsDataSingle(payload=hop_payload))
|
||||
if not is_trampoline:
|
||||
amt += route_edge.fee_for_edge(amt)
|
||||
cltv += route_edge.cltv_expiry_delta
|
||||
cltv_abs += route_edge.cltv_delta
|
||||
hops_data.reverse()
|
||||
return hops_data, amt, cltv
|
||||
return hops_data, amt, cltv_abs
|
||||
|
||||
|
||||
def _generate_filler(key_type: bytes, hops_data: Sequence[OnionHopsDataSingle],
|
||||
|
||||
+64
-42
@@ -38,7 +38,7 @@ from .lnutil import (Outpoint, LocalConfig, RECEIVED, UpdateAddHtlc, ChannelConf
|
||||
funding_output_script, get_per_commitment_secret_from_seed,
|
||||
secret_to_pubkey, PaymentFailure, LnFeatures,
|
||||
LOCAL, REMOTE, HTLCOwner,
|
||||
ln_compare_features, privkey_to_pubkey, MIN_FINAL_CLTV_EXPIRY_ACCEPTED,
|
||||
ln_compare_features, privkey_to_pubkey, MIN_FINAL_CLTV_DELTA_ACCEPTED,
|
||||
LightningPeerConnectionClosed, HandshakeFailed,
|
||||
RemoteMisbehaving, ShortChannelID,
|
||||
IncompatibleLightningFeatures, derive_payment_secret_from_payment_preimage,
|
||||
@@ -1476,25 +1476,25 @@ class Peer(Logger):
|
||||
amount_msat: int,
|
||||
total_msat: int,
|
||||
payment_hash: bytes,
|
||||
min_final_cltv_expiry: int,
|
||||
min_final_cltv_delta: int,
|
||||
payment_secret: bytes,
|
||||
trampoline_onion: Optional[OnionPacket] = None,
|
||||
):
|
||||
# add features learned during "init" for direct neighbour:
|
||||
route[0].node_features |= self.features
|
||||
local_height = self.network.get_local_height()
|
||||
final_cltv = local_height + min_final_cltv_expiry
|
||||
hops_data, amount_msat, cltv = calc_hops_data_for_payment(
|
||||
final_cltv_abs = local_height + min_final_cltv_delta
|
||||
hops_data, amount_msat, cltv_abs = calc_hops_data_for_payment(
|
||||
route,
|
||||
amount_msat,
|
||||
final_cltv,
|
||||
final_cltv_abs=final_cltv_abs,
|
||||
total_msat=total_msat,
|
||||
payment_secret=payment_secret)
|
||||
num_hops = len(hops_data)
|
||||
self.logger.info(f"lnpeer.pay len(route)={len(route)}")
|
||||
for i in range(len(route)):
|
||||
self.logger.info(f" {i}: edge={route[i].short_channel_id} hop_data={hops_data[i]!r}")
|
||||
assert final_cltv <= cltv, (final_cltv, cltv)
|
||||
assert final_cltv_abs <= cltv_abs, (final_cltv_abs, cltv_abs)
|
||||
session_key = os.urandom(32) # session_key
|
||||
# if we are forwarding a trampoline payment, add trampoline onion
|
||||
if trampoline_onion:
|
||||
@@ -1517,12 +1517,21 @@ class Peer(Logger):
|
||||
onion = new_onion_packet(payment_path_pubkeys, session_key, hops_data, associated_data=payment_hash) # must use another sessionkey
|
||||
self.logger.info(f"starting payment. len(route)={len(hops_data)}.")
|
||||
# create htlc
|
||||
if cltv > local_height + lnutil.NBLOCK_CLTV_EXPIRY_TOO_FAR_INTO_FUTURE:
|
||||
raise PaymentFailure(f"htlc expiry too far into future. (in {cltv-local_height} blocks)")
|
||||
return onion, amount_msat, cltv, session_key
|
||||
if cltv_abs > local_height + lnutil.NBLOCK_CLTV_DELTA_TOO_FAR_INTO_FUTURE:
|
||||
raise PaymentFailure(f"htlc expiry too far into future. (in {cltv_abs-local_height} blocks)")
|
||||
return onion, amount_msat, cltv_abs, session_key
|
||||
|
||||
def send_htlc(self, chan, payment_hash, amount_msat, cltv, onion, session_key=None) -> UpdateAddHtlc:
|
||||
htlc = UpdateAddHtlc(amount_msat=amount_msat, payment_hash=payment_hash, cltv_expiry=cltv, timestamp=int(time.time()))
|
||||
def send_htlc(
|
||||
self,
|
||||
*,
|
||||
chan: Channel,
|
||||
payment_hash: bytes,
|
||||
amount_msat: int,
|
||||
cltv_abs: int,
|
||||
onion: OnionPacket,
|
||||
session_key: Optional[bytes] = None,
|
||||
) -> UpdateAddHtlc:
|
||||
htlc = UpdateAddHtlc(amount_msat=amount_msat, payment_hash=payment_hash, cltv_abs=cltv_abs, timestamp=int(time.time()))
|
||||
htlc = chan.add_htlc(htlc)
|
||||
if session_key:
|
||||
chan.set_onion_key(htlc.htlc_id, session_key) # should it be the outer onion secret?
|
||||
@@ -1531,7 +1540,7 @@ class Peer(Logger):
|
||||
"update_add_htlc",
|
||||
channel_id=chan.channel_id,
|
||||
id=htlc.htlc_id,
|
||||
cltv_expiry=htlc.cltv_expiry,
|
||||
cltv_expiry=htlc.cltv_abs,
|
||||
amount_msat=htlc.amount_msat,
|
||||
payment_hash=htlc.payment_hash,
|
||||
onion_routing_packet=onion.to_bytes())
|
||||
@@ -1544,7 +1553,7 @@ class Peer(Logger):
|
||||
amount_msat: int,
|
||||
total_msat: int,
|
||||
payment_hash: bytes,
|
||||
min_final_cltv_expiry: int,
|
||||
min_final_cltv_delta: int,
|
||||
payment_secret: bytes,
|
||||
trampoline_onion: Optional[OnionPacket] = None,
|
||||
) -> UpdateAddHtlc:
|
||||
@@ -1553,16 +1562,23 @@ class Peer(Logger):
|
||||
assert len(route) > 0
|
||||
if not chan.can_send_update_add_htlc():
|
||||
raise PaymentFailure("Channel cannot send update_add_htlc")
|
||||
onion, amount_msat, cltv, session_key = self.create_onion_for_route(
|
||||
onion, amount_msat, cltv_abs, session_key = self.create_onion_for_route(
|
||||
route=route,
|
||||
amount_msat=amount_msat,
|
||||
total_msat=total_msat,
|
||||
payment_hash=payment_hash,
|
||||
min_final_cltv_expiry=min_final_cltv_expiry,
|
||||
min_final_cltv_delta=min_final_cltv_delta,
|
||||
payment_secret=payment_secret,
|
||||
trampoline_onion=trampoline_onion
|
||||
)
|
||||
htlc = self.send_htlc(chan, payment_hash, amount_msat, cltv, onion, session_key=session_key)
|
||||
htlc = self.send_htlc(
|
||||
chan=chan,
|
||||
payment_hash=payment_hash,
|
||||
amount_msat=amount_msat,
|
||||
cltv_abs=cltv_abs,
|
||||
onion=onion,
|
||||
session_key=session_key,
|
||||
)
|
||||
return htlc
|
||||
|
||||
def send_revoke_and_ack(self, chan: Channel):
|
||||
@@ -1619,21 +1635,21 @@ class Peer(Logger):
|
||||
def on_update_add_htlc(self, chan: Channel, payload):
|
||||
payment_hash = payload["payment_hash"]
|
||||
htlc_id = payload["id"]
|
||||
cltv_expiry = payload["cltv_expiry"]
|
||||
cltv_abs = payload["cltv_expiry"]
|
||||
amount_msat_htlc = payload["amount_msat"]
|
||||
onion_packet = payload["onion_routing_packet"]
|
||||
htlc = UpdateAddHtlc(
|
||||
amount_msat=amount_msat_htlc,
|
||||
payment_hash=payment_hash,
|
||||
cltv_expiry=cltv_expiry,
|
||||
cltv_abs=cltv_abs,
|
||||
timestamp=int(time.time()),
|
||||
htlc_id=htlc_id)
|
||||
self.logger.info(f"on_update_add_htlc. chan {chan.short_channel_id}. htlc={str(htlc)}")
|
||||
if chan.get_state() != ChannelState.OPEN:
|
||||
raise RemoteMisbehaving(f"received update_add_htlc while chan.get_state() != OPEN. state was {chan.get_state()!r}")
|
||||
if cltv_expiry > bitcoin.NLOCKTIME_BLOCKHEIGHT_MAX:
|
||||
if cltv_abs > bitcoin.NLOCKTIME_BLOCKHEIGHT_MAX:
|
||||
self.schedule_force_closing(chan.channel_id)
|
||||
raise RemoteMisbehaving(f"received update_add_htlc with cltv_expiry > BLOCKHEIGHT_MAX. value was {cltv_expiry}")
|
||||
raise RemoteMisbehaving(f"received update_add_htlc with {cltv_abs=} > BLOCKHEIGHT_MAX")
|
||||
# add htlc
|
||||
chan.receive_htlc(htlc, onion_packet)
|
||||
util.trigger_callback('htlc_added', chan, htlc, RECEIVED)
|
||||
@@ -1674,7 +1690,7 @@ class Peer(Logger):
|
||||
except Exception:
|
||||
raise OnionRoutingFailure(code=OnionFailureCode.INVALID_ONION_PAYLOAD, data=b'\x00\x00\x00')
|
||||
try:
|
||||
next_cltv_expiry = processed_onion.hop_data.payload["outgoing_cltv_value"]["outgoing_cltv_value"]
|
||||
next_cltv_abs = processed_onion.hop_data.payload["outgoing_cltv_value"]["outgoing_cltv_value"]
|
||||
except Exception:
|
||||
raise OnionRoutingFailure(code=OnionFailureCode.INVALID_ONION_PAYLOAD, data=b'\x00\x00\x00')
|
||||
next_chan = self.lnworker.get_channel_by_short_id(next_chan_scid)
|
||||
@@ -1693,16 +1709,16 @@ class Peer(Logger):
|
||||
if not next_chan.can_pay(next_amount_msat_htlc):
|
||||
log_fail_reason(f"transient error (likely due to insufficient funds): not next_chan.can_pay(amt)")
|
||||
raise OnionRoutingFailure(code=OnionFailureCode.TEMPORARY_CHANNEL_FAILURE, data=outgoing_chan_upd_message)
|
||||
if htlc.cltv_expiry - next_cltv_expiry < next_chan.forwarding_cltv_expiry_delta:
|
||||
if htlc.cltv_abs - next_cltv_abs < next_chan.forwarding_cltv_delta:
|
||||
log_fail_reason(
|
||||
f"INCORRECT_CLTV_EXPIRY. "
|
||||
f"{htlc.cltv_expiry=} - {next_cltv_expiry=} < {next_chan.forwarding_cltv_expiry_delta=}")
|
||||
data = htlc.cltv_expiry.to_bytes(4, byteorder="big") + outgoing_chan_upd_message
|
||||
f"{htlc.cltv_abs=} - {next_cltv_abs=} < {next_chan.forwarding_cltv_delta=}")
|
||||
data = htlc.cltv_abs.to_bytes(4, byteorder="big") + outgoing_chan_upd_message
|
||||
raise OnionRoutingFailure(code=OnionFailureCode.INCORRECT_CLTV_EXPIRY, data=data)
|
||||
if htlc.cltv_expiry - lnutil.MIN_FINAL_CLTV_EXPIRY_ACCEPTED <= local_height \
|
||||
or next_cltv_expiry <= local_height:
|
||||
if htlc.cltv_abs - lnutil.MIN_FINAL_CLTV_DELTA_ACCEPTED <= local_height \
|
||||
or next_cltv_abs <= local_height:
|
||||
raise OnionRoutingFailure(code=OnionFailureCode.EXPIRY_TOO_SOON, data=outgoing_chan_upd_message)
|
||||
if max(htlc.cltv_expiry, next_cltv_expiry) > local_height + lnutil.NBLOCK_CLTV_EXPIRY_TOO_FAR_INTO_FUTURE:
|
||||
if max(htlc.cltv_abs, next_cltv_abs) > local_height + lnutil.NBLOCK_CLTV_DELTA_TOO_FAR_INTO_FUTURE:
|
||||
raise OnionRoutingFailure(code=OnionFailureCode.EXPIRY_TOO_FAR, data=b'')
|
||||
forwarding_fees = fee_for_edge_msat(
|
||||
forwarded_amount_msat=next_amount_msat_htlc,
|
||||
@@ -1722,7 +1738,13 @@ class Peer(Logger):
|
||||
log_fail_reason(f"next_peer offline ({next_chan.node_id.hex()})")
|
||||
raise OnionRoutingFailure(code=OnionFailureCode.TEMPORARY_CHANNEL_FAILURE, data=outgoing_chan_upd_message)
|
||||
try:
|
||||
next_htlc = next_peer.send_htlc(next_chan, htlc.payment_hash, next_amount_msat_htlc, next_cltv_expiry, processed_onion.next_packet)
|
||||
next_htlc = next_peer.send_htlc(
|
||||
chan=next_chan,
|
||||
payment_hash=htlc.payment_hash,
|
||||
amount_msat=next_amount_msat_htlc,
|
||||
cltv_abs=next_cltv_abs,
|
||||
onion=processed_onion.next_packet,
|
||||
)
|
||||
except BaseException as e:
|
||||
log_fail_reason(f"error sending message to next_peer={next_chan.node_id.hex()}")
|
||||
raise OnionRoutingFailure(code=OnionFailureCode.TEMPORARY_CHANNEL_FAILURE, data=outgoing_chan_upd_message)
|
||||
@@ -1732,7 +1754,7 @@ class Peer(Logger):
|
||||
async def maybe_forward_trampoline(
|
||||
self, *,
|
||||
payment_hash: bytes,
|
||||
cltv_expiry: int,
|
||||
inc_cltv_abs: int,
|
||||
outer_onion: ProcessedOnionPacket,
|
||||
trampoline_onion: ProcessedOnionPacket):
|
||||
|
||||
@@ -1751,7 +1773,7 @@ class Peer(Logger):
|
||||
try:
|
||||
outgoing_node_id = payload["outgoing_node_id"]["outgoing_node_id"]
|
||||
amt_to_forward = payload["amt_to_forward"]["amt_to_forward"]
|
||||
cltv_from_onion = payload["outgoing_cltv_value"]["outgoing_cltv_value"]
|
||||
out_cltv_abs = payload["outgoing_cltv_value"]["outgoing_cltv_value"]
|
||||
if "invoice_features" in payload:
|
||||
self.logger.info('forward_trampoline: legacy')
|
||||
next_trampoline_onion = None
|
||||
@@ -1777,11 +1799,11 @@ class Peer(Logger):
|
||||
|
||||
# these are the fee/cltv paid by the sender
|
||||
# pay_to_node will raise if they are not sufficient
|
||||
trampoline_cltv_delta = cltv_expiry - cltv_from_onion # cltv budget
|
||||
trampoline_cltv_delta = inc_cltv_abs - out_cltv_abs # cltv budget
|
||||
total_msat = outer_onion.hop_data.payload["payment_data"]["total_msat"]
|
||||
trampoline_fee = total_msat - amt_to_forward
|
||||
self.logger.info(f'trampoline forwarding. fee_budget={trampoline_fee}')
|
||||
self.logger.info(f'trampoline forwarding. cltv_budget={trampoline_cltv_delta}. (inc={cltv_expiry}. out={cltv_from_onion})')
|
||||
self.logger.info(f'trampoline forwarding. cltv_budget={trampoline_cltv_delta}. (inc={inc_cltv_abs}. out={out_cltv_abs})')
|
||||
# To convert abs vs rel cltvs, we need to guess blockheight used by original sender as "current blockheight".
|
||||
# Blocks might have been mined since.
|
||||
# - if we skew towards the past, we decrease our own cltv_budget accordingly (which is ok)
|
||||
@@ -1789,7 +1811,7 @@ class Peer(Logger):
|
||||
# which can result in them failing the payment.
|
||||
# So we skew towards the past and guess that there has been 1 new block mined since the payment began:
|
||||
local_height_of_onion_creator = self.network.get_local_height() - 1
|
||||
cltv_budget_for_rest_of_route = cltv_from_onion - local_height_of_onion_creator
|
||||
cltv_budget_for_rest_of_route = out_cltv_abs - local_height_of_onion_creator
|
||||
|
||||
try:
|
||||
await self.lnworker.pay_to_node(
|
||||
@@ -1797,10 +1819,10 @@ class Peer(Logger):
|
||||
payment_hash=payment_hash,
|
||||
payment_secret=payment_secret,
|
||||
amount_to_pay=amt_to_forward,
|
||||
# FIXME this API (min_cltv_expiry) is confusing. The value will be added to local_height
|
||||
# FIXME this API (min_final_cltv_delta) is confusing. The value will be added to local_height
|
||||
# to form the abs cltv used on the last edge on the path to the *next trampoline* node.
|
||||
# We should rewrite pay_to_node to operate on a cltv-budget (and fee-budget).
|
||||
min_cltv_expiry=cltv_budget_for_rest_of_route,
|
||||
min_final_cltv_delta=cltv_budget_for_rest_of_route,
|
||||
r_tags=r_tags,
|
||||
invoice_features=invoice_features,
|
||||
fwd_trampoline_onion=next_trampoline_onion,
|
||||
@@ -1863,20 +1885,20 @@ class Peer(Logger):
|
||||
exc_incorrect_or_unknown_pd = OnionRoutingFailure(
|
||||
code=OnionFailureCode.INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS,
|
||||
data=amt_to_forward.to_bytes(8, byteorder="big") + local_height.to_bytes(4, byteorder="big"))
|
||||
if local_height + MIN_FINAL_CLTV_EXPIRY_ACCEPTED > htlc.cltv_expiry:
|
||||
log_fail_reason(f"htlc.cltv_expiry is unreasonably close")
|
||||
if local_height + MIN_FINAL_CLTV_DELTA_ACCEPTED > htlc.cltv_abs:
|
||||
log_fail_reason(f"htlc.cltv_abs is unreasonably close")
|
||||
raise exc_incorrect_or_unknown_pd
|
||||
try:
|
||||
cltv_from_onion = processed_onion.hop_data.payload["outgoing_cltv_value"]["outgoing_cltv_value"]
|
||||
cltv_abs_from_onion = processed_onion.hop_data.payload["outgoing_cltv_value"]["outgoing_cltv_value"]
|
||||
except Exception:
|
||||
log_fail_reason(f"'outgoing_cltv_value' missing from onion")
|
||||
raise OnionRoutingFailure(code=OnionFailureCode.INVALID_ONION_PAYLOAD, data=b'\x00\x00\x00')
|
||||
|
||||
if cltv_from_onion > htlc.cltv_expiry:
|
||||
log_fail_reason(f"cltv_from_onion != htlc.cltv_expiry")
|
||||
if cltv_abs_from_onion > htlc.cltv_abs:
|
||||
log_fail_reason(f"cltv_abs_from_onion != htlc.cltv_abs")
|
||||
raise OnionRoutingFailure(
|
||||
code=OnionFailureCode.FINAL_INCORRECT_CLTV_EXPIRY,
|
||||
data=htlc.cltv_expiry.to_bytes(4, byteorder="big"))
|
||||
data=htlc.cltv_abs.to_bytes(4, byteorder="big"))
|
||||
try:
|
||||
total_msat = processed_onion.hop_data.payload["payment_data"]["total_msat"]
|
||||
except Exception:
|
||||
@@ -1943,7 +1965,7 @@ class Peer(Logger):
|
||||
else:
|
||||
callback = lambda: self.maybe_forward_trampoline(
|
||||
payment_hash=payment_hash,
|
||||
cltv_expiry=htlc.cltv_expiry, # TODO: use max or enforce same value across mpp parts
|
||||
inc_cltv_abs=htlc.cltv_abs, # TODO: use max or enforce same value across mpp parts
|
||||
outer_onion=processed_onion,
|
||||
trampoline_onion=trampoline_onion)
|
||||
return None, callback
|
||||
|
||||
+10
-9
@@ -35,7 +35,7 @@ from math import inf
|
||||
from .util import profiler, with_lock
|
||||
from .logging import Logger
|
||||
from .lnutil import (NUM_MAX_EDGES_IN_PAYMENT_PATH, ShortChannelID, LnFeatures,
|
||||
NBLOCK_CLTV_EXPIRY_TOO_FAR_INTO_FUTURE)
|
||||
NBLOCK_CLTV_DELTA_TOO_FAR_INTO_FUTURE)
|
||||
from .channel_db import ChannelDB, Policy, NodeInfo
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -75,7 +75,7 @@ class PathEdge:
|
||||
class RouteEdge(PathEdge):
|
||||
fee_base_msat = attr.ib(type=int, kw_only=True)
|
||||
fee_proportional_millionths = attr.ib(type=int, kw_only=True)
|
||||
cltv_expiry_delta = attr.ib(type=int, kw_only=True)
|
||||
cltv_delta = attr.ib(type=int, kw_only=True)
|
||||
node_features = attr.ib(type=int, kw_only=True, repr=lambda val: str(int(val))) # note: for end node!
|
||||
|
||||
def fee_for_edge(self, amount_msat: int) -> int:
|
||||
@@ -102,13 +102,13 @@ class RouteEdge(PathEdge):
|
||||
short_channel_id=ShortChannelID.normalize(short_channel_id),
|
||||
fee_base_msat=channel_policy.fee_base_msat,
|
||||
fee_proportional_millionths=channel_policy.fee_proportional_millionths,
|
||||
cltv_expiry_delta=channel_policy.cltv_expiry_delta,
|
||||
cltv_delta=channel_policy.cltv_delta,
|
||||
node_features=node_info.features if node_info else 0)
|
||||
|
||||
def is_sane_to_use(self, amount_msat: int) -> bool:
|
||||
# TODO revise ad-hoc heuristics
|
||||
# cltv cannot be more than 2 weeks
|
||||
if self.cltv_expiry_delta > 14 * 144:
|
||||
if self.cltv_delta > 14 * 144:
|
||||
return False
|
||||
total_fee = self.fee_for_edge(amount_msat)
|
||||
if not is_fee_sane(total_fee, payment_amount_msat=amount_msat):
|
||||
@@ -135,23 +135,24 @@ class TrampolineEdge(RouteEdge):
|
||||
|
||||
LNPaymentPath = Sequence[PathEdge]
|
||||
LNPaymentRoute = Sequence[RouteEdge]
|
||||
LNPaymentTRoute = Sequence[TrampolineEdge]
|
||||
|
||||
|
||||
def is_route_sane_to_use(route: LNPaymentRoute, invoice_amount_msat: int, min_final_cltv_expiry: int) -> bool:
|
||||
def is_route_sane_to_use(route: LNPaymentRoute, invoice_amount_msat: int, min_final_cltv_delta: int) -> bool:
|
||||
"""Run some sanity checks on the whole route, before attempting to use it.
|
||||
called when we are paying; so e.g. lower cltv is better
|
||||
"""
|
||||
if len(route) > NUM_MAX_EDGES_IN_PAYMENT_PATH:
|
||||
return False
|
||||
amt = invoice_amount_msat
|
||||
cltv = min_final_cltv_expiry
|
||||
cltv_delta = min_final_cltv_delta
|
||||
for route_edge in reversed(route[1:]):
|
||||
if not route_edge.is_sane_to_use(amt): return False
|
||||
amt += route_edge.fee_for_edge(amt)
|
||||
cltv += route_edge.cltv_expiry_delta
|
||||
cltv_delta += route_edge.cltv_delta
|
||||
total_fee = amt - invoice_amount_msat
|
||||
# TODO revise ad-hoc heuristics
|
||||
if cltv > NBLOCK_CLTV_EXPIRY_TOO_FAR_INTO_FUTURE:
|
||||
if cltv_delta > NBLOCK_CLTV_DELTA_TOO_FAR_INTO_FUTURE:
|
||||
return False
|
||||
# FIXME in case of MPP, the fee checks are done independently for each part,
|
||||
# which is ok for the proportional checks but not for the absolute ones.
|
||||
@@ -527,7 +528,7 @@ class LNPathFinder(Logger):
|
||||
if ignore_costs:
|
||||
return DEFAULT_PENALTY_BASE_MSAT, 0
|
||||
fee_msat = route_edge.fee_for_edge(payment_amt_msat)
|
||||
cltv_cost = route_edge.cltv_expiry_delta * payment_amt_msat * 15 / 1_000_000_000
|
||||
cltv_cost = route_edge.cltv_delta * payment_amt_msat * 15 / 1_000_000_000
|
||||
# the liquidty penalty takes care we favor edges that should be able to forward
|
||||
# the payment and penalize edges that cannot
|
||||
liquidity_penalty = self.liquidity_hints.penalty(start_node, end_node, short_channel_id, payment_amt_msat)
|
||||
|
||||
+13
-13
@@ -33,7 +33,7 @@ _logger = get_logger(__name__)
|
||||
class SweepInfo(NamedTuple):
|
||||
name: str
|
||||
csv_delay: int
|
||||
cltv_expiry: int
|
||||
cltv_abs: int
|
||||
gen_tx: Callable[[], Optional[Transaction]]
|
||||
|
||||
|
||||
@@ -177,7 +177,7 @@ def create_sweeptx_for_their_revoked_htlc(
|
||||
return SweepInfo(
|
||||
name='redeem_htlc2',
|
||||
csv_delay=0,
|
||||
cltv_expiry=0,
|
||||
cltv_abs=0,
|
||||
gen_tx=gen_tx)
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@ def create_sweeptxs_for_our_ctx(
|
||||
txs[prevout] = SweepInfo(
|
||||
name='our_ctx_to_local',
|
||||
csv_delay=to_self_delay,
|
||||
cltv_expiry=0,
|
||||
cltv_abs=0,
|
||||
gen_tx=sweep_tx)
|
||||
we_breached = ctn < chan.get_oldest_unrevoked_ctn(LOCAL)
|
||||
if we_breached:
|
||||
@@ -277,12 +277,12 @@ def create_sweeptxs_for_our_ctx(
|
||||
txs[htlc_tx.inputs()[0].prevout.to_str()] = SweepInfo(
|
||||
name='first-stage-htlc',
|
||||
csv_delay=0,
|
||||
cltv_expiry=htlc_tx.locktime,
|
||||
cltv_abs=htlc_tx.locktime,
|
||||
gen_tx=lambda: htlc_tx)
|
||||
txs[htlc_tx.txid() + ':0'] = SweepInfo(
|
||||
name='second-stage-htlc',
|
||||
csv_delay=to_self_delay,
|
||||
cltv_expiry=0,
|
||||
cltv_abs=0,
|
||||
gen_tx=sweep_tx)
|
||||
|
||||
# offered HTLCs, in our ctx --> "timeout"
|
||||
@@ -381,7 +381,7 @@ def create_sweeptxs_for_their_ctx(
|
||||
txs[tx.inputs()[0].prevout.to_str()] = SweepInfo(
|
||||
name='to_local_for_revoked_ctx',
|
||||
csv_delay=0,
|
||||
cltv_expiry=0,
|
||||
cltv_abs=0,
|
||||
gen_tx=gen_tx)
|
||||
# prep
|
||||
our_htlc_privkey = derive_privkey(secret=int.from_bytes(our_conf.htlc_basepoint.privkey, 'big'), per_commitment_point=their_pcp)
|
||||
@@ -400,9 +400,9 @@ def create_sweeptxs_for_their_ctx(
|
||||
remote_htlc_pubkey=our_htlc_privkey.get_public_key_bytes(compressed=True),
|
||||
local_htlc_pubkey=their_htlc_pubkey,
|
||||
payment_hash=htlc.payment_hash,
|
||||
cltv_expiry=htlc.cltv_expiry)
|
||||
cltv_abs=htlc.cltv_abs)
|
||||
|
||||
cltv_expiry = htlc.cltv_expiry if is_received_htlc and not is_revocation else 0
|
||||
cltv_abs = htlc.cltv_abs if is_received_htlc and not is_revocation else 0
|
||||
prevout = ctx.txid() + ':%d'%ctx_output_idx
|
||||
sweep_tx = lambda: create_sweeptx_their_ctx_htlc(
|
||||
ctx=ctx,
|
||||
@@ -412,12 +412,12 @@ def create_sweeptxs_for_their_ctx(
|
||||
output_idx=ctx_output_idx,
|
||||
privkey=our_revocation_privkey if is_revocation else our_htlc_privkey.get_secret_bytes(),
|
||||
is_revocation=is_revocation,
|
||||
cltv_expiry=cltv_expiry,
|
||||
cltv_abs=cltv_abs,
|
||||
config=chan.lnworker.config)
|
||||
txs[prevout] = SweepInfo(
|
||||
name=f'their_ctx_htlc_{ctx_output_idx}',
|
||||
csv_delay=0,
|
||||
cltv_expiry=cltv_expiry,
|
||||
cltv_abs=cltv_abs,
|
||||
gen_tx=sweep_tx)
|
||||
# received HTLCs, in their ctx --> "timeout"
|
||||
# offered HTLCs, in their ctx --> "success"
|
||||
@@ -480,8 +480,8 @@ def create_sweeptx_their_ctx_htlc(
|
||||
ctx: Transaction, witness_script: bytes, sweep_address: str,
|
||||
preimage: Optional[bytes], output_idx: int,
|
||||
privkey: bytes, is_revocation: bool,
|
||||
cltv_expiry: int, config: SimpleConfig) -> Optional[PartialTransaction]:
|
||||
assert type(cltv_expiry) is int
|
||||
cltv_abs: int, config: SimpleConfig) -> Optional[PartialTransaction]:
|
||||
assert type(cltv_abs) is int
|
||||
preimage = preimage or b'' # preimage is required iff (not is_revocation and htlc is offered)
|
||||
val = ctx.outputs()[output_idx].value
|
||||
prevout = TxOutpoint(txid=bfh(ctx.txid()), out_idx=output_idx)
|
||||
@@ -495,7 +495,7 @@ def create_sweeptx_their_ctx_htlc(
|
||||
outvalue = val - fee
|
||||
if outvalue <= dust_threshold(): return None
|
||||
sweep_outputs = [PartialTxOutput.from_address_and_value(sweep_address, outvalue)]
|
||||
tx = PartialTransaction.from_io(sweep_inputs, sweep_outputs, version=2, locktime=cltv_expiry)
|
||||
tx = PartialTransaction.from_io(sweep_inputs, sweep_outputs, version=2, locktime=cltv_abs)
|
||||
sig = bfh(tx.sign_txin(0, privkey))
|
||||
if not is_revocation:
|
||||
witness = construct_witness([sig, preimage, witness_script])
|
||||
|
||||
+48
-30
@@ -446,20 +446,20 @@ 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_EXPIRY_ACCEPTED = 144
|
||||
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_EXPIRY_FOR_INVOICE = MIN_FINAL_CLTV_EXPIRY_ACCEPTED + 3
|
||||
MIN_FINAL_CLTV_DELTA_FOR_INVOICE = MIN_FINAL_CLTV_DELTA_ACCEPTED + 3
|
||||
|
||||
# the deadline for offered HTLCs:
|
||||
# the deadline after which the channel has to be failed and timed out on-chain
|
||||
NBLOCK_DEADLINE_AFTER_EXPIRY_FOR_OFFERED_HTLCS = 1
|
||||
NBLOCK_DEADLINE_DELTA_AFTER_EXPIRY_FOR_OFFERED_HTLCS = 1
|
||||
|
||||
# the deadline for received HTLCs this node has fulfilled:
|
||||
# the deadline after which the channel has to be failed and the HTLC fulfilled on-chain before its cltv_expiry
|
||||
NBLOCK_DEADLINE_BEFORE_EXPIRY_FOR_RECEIVED_HTLCS = 72
|
||||
NBLOCK_DEADLINE_DELTA_BEFORE_EXPIRY_FOR_RECEIVED_HTLCS = 72
|
||||
|
||||
NBLOCK_CLTV_EXPIRY_TOO_FAR_INTO_FUTURE = 28 * 144
|
||||
NBLOCK_CLTV_DELTA_TOO_FAR_INTO_FUTURE = 28 * 144
|
||||
|
||||
MAXIMUM_REMOTE_TO_SELF_DELAY_ACCEPTED = 2016
|
||||
|
||||
@@ -621,14 +621,19 @@ def make_htlc_tx_inputs(htlc_output_txid: str, htlc_output_index: int,
|
||||
c_inputs = [txin]
|
||||
return c_inputs
|
||||
|
||||
def make_htlc_tx(*, cltv_expiry: int, inputs: List[PartialTxInput], output: PartialTxOutput) -> PartialTransaction:
|
||||
assert type(cltv_expiry) is int
|
||||
def make_htlc_tx(*, cltv_abs: int, inputs: List[PartialTxInput], output: PartialTxOutput) -> PartialTransaction:
|
||||
assert type(cltv_abs) is int
|
||||
c_outputs = [output]
|
||||
tx = PartialTransaction.from_io(inputs, c_outputs, locktime=cltv_expiry, version=2)
|
||||
tx = PartialTransaction.from_io(inputs, c_outputs, locktime=cltv_abs, version=2)
|
||||
return tx
|
||||
|
||||
def make_offered_htlc(revocation_pubkey: bytes, remote_htlcpubkey: bytes,
|
||||
local_htlcpubkey: bytes, payment_hash: bytes) -> bytes:
|
||||
def make_offered_htlc(
|
||||
*,
|
||||
revocation_pubkey: bytes,
|
||||
remote_htlcpubkey: bytes,
|
||||
local_htlcpubkey: bytes,
|
||||
payment_hash: bytes,
|
||||
) -> bytes:
|
||||
assert type(revocation_pubkey) is bytes
|
||||
assert type(remote_htlcpubkey) is bytes
|
||||
assert type(local_htlcpubkey) is bytes
|
||||
@@ -663,11 +668,17 @@ def make_offered_htlc(revocation_pubkey: bytes, remote_htlcpubkey: bytes,
|
||||
]))
|
||||
return script
|
||||
|
||||
def make_received_htlc(revocation_pubkey: bytes, remote_htlcpubkey: bytes,
|
||||
local_htlcpubkey: bytes, payment_hash: bytes, cltv_expiry: int) -> bytes:
|
||||
def make_received_htlc(
|
||||
*,
|
||||
revocation_pubkey: bytes,
|
||||
remote_htlcpubkey: bytes,
|
||||
local_htlcpubkey: bytes,
|
||||
payment_hash: bytes,
|
||||
cltv_abs: int,
|
||||
) -> bytes:
|
||||
for i in [revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, payment_hash]:
|
||||
assert type(i) is bytes
|
||||
assert type(cltv_expiry) is int
|
||||
assert type(cltv_abs) is int
|
||||
|
||||
script = bfh(construct_script([
|
||||
opcodes.OP_DUP,
|
||||
@@ -693,7 +704,7 @@ def make_received_htlc(revocation_pubkey: bytes, remote_htlcpubkey: bytes,
|
||||
opcodes.OP_CHECKMULTISIG,
|
||||
opcodes.OP_ELSE,
|
||||
opcodes.OP_DROP,
|
||||
cltv_expiry,
|
||||
cltv_abs,
|
||||
opcodes.OP_CHECKLOCKTIMEVERIFY,
|
||||
opcodes.OP_DROP,
|
||||
opcodes.OP_CHECKSIG,
|
||||
@@ -764,14 +775,21 @@ WITNESS_TEMPLATE_RECEIVED_HTLC = [
|
||||
]
|
||||
|
||||
|
||||
def make_htlc_output_witness_script(is_received_htlc: bool, remote_revocation_pubkey: bytes, remote_htlc_pubkey: bytes,
|
||||
local_htlc_pubkey: bytes, payment_hash: bytes, cltv_expiry: Optional[int]) -> bytes:
|
||||
def make_htlc_output_witness_script(
|
||||
*,
|
||||
is_received_htlc: bool,
|
||||
remote_revocation_pubkey: bytes,
|
||||
remote_htlc_pubkey: bytes,
|
||||
local_htlc_pubkey: bytes,
|
||||
payment_hash: bytes,
|
||||
cltv_abs: Optional[int],
|
||||
) -> bytes:
|
||||
if is_received_htlc:
|
||||
return make_received_htlc(revocation_pubkey=remote_revocation_pubkey,
|
||||
remote_htlcpubkey=remote_htlc_pubkey,
|
||||
local_htlcpubkey=local_htlc_pubkey,
|
||||
payment_hash=payment_hash,
|
||||
cltv_expiry=cltv_expiry)
|
||||
cltv_abs=cltv_abs)
|
||||
else:
|
||||
return make_offered_htlc(revocation_pubkey=remote_revocation_pubkey,
|
||||
remote_htlcpubkey=remote_htlc_pubkey,
|
||||
@@ -789,7 +807,7 @@ def get_ordered_channel_configs(chan: 'AbstractChannel', for_us: bool) -> Tuple[
|
||||
def possible_output_idxs_of_htlc_in_ctx(*, chan: 'Channel', pcp: bytes, subject: 'HTLCOwner',
|
||||
htlc_direction: 'Direction', ctx: Transaction,
|
||||
htlc: 'UpdateAddHtlc') -> Set[int]:
|
||||
amount_msat, cltv_expiry, payment_hash = htlc.amount_msat, htlc.cltv_expiry, htlc.payment_hash
|
||||
amount_msat, cltv_abs, payment_hash = htlc.amount_msat, htlc.cltv_abs, htlc.payment_hash
|
||||
for_us = subject == LOCAL
|
||||
conf, other_conf = get_ordered_channel_configs(chan=chan, for_us=for_us)
|
||||
|
||||
@@ -801,7 +819,7 @@ def possible_output_idxs_of_htlc_in_ctx(*, chan: 'Channel', pcp: bytes, subject:
|
||||
remote_htlc_pubkey=other_htlc_pubkey,
|
||||
local_htlc_pubkey=htlc_pubkey,
|
||||
payment_hash=payment_hash,
|
||||
cltv_expiry=cltv_expiry)
|
||||
cltv_abs=cltv_abs)
|
||||
htlc_address = redeem_script_to_address('p2wsh', preimage_script.hex())
|
||||
candidates = ctx.get_output_idxs_from_address(htlc_address)
|
||||
return {output_idx for output_idx in candidates
|
||||
@@ -814,9 +832,9 @@ def map_htlcs_to_ctx_output_idxs(*, chan: 'Channel', ctx: Transaction, pcp: byte
|
||||
htlc_to_ctx_output_idx_map = {} # type: Dict[Tuple[Direction, UpdateAddHtlc], int]
|
||||
unclaimed_ctx_output_idxs = set(range(len(ctx.outputs())))
|
||||
offered_htlcs = chan.included_htlcs(subject, SENT, ctn=ctn)
|
||||
offered_htlcs.sort(key=lambda htlc: htlc.cltv_expiry)
|
||||
offered_htlcs.sort(key=lambda htlc: htlc.cltv_abs)
|
||||
received_htlcs = chan.included_htlcs(subject, RECEIVED, ctn=ctn)
|
||||
received_htlcs.sort(key=lambda htlc: htlc.cltv_expiry)
|
||||
received_htlcs.sort(key=lambda htlc: htlc.cltv_abs)
|
||||
for direction, htlcs in zip([SENT, RECEIVED], [offered_htlcs, received_htlcs]):
|
||||
for htlc in htlcs:
|
||||
cands = sorted(possible_output_idxs_of_htlc_in_ctx(chan=chan,
|
||||
@@ -841,7 +859,7 @@ def map_htlcs_to_ctx_output_idxs(*, chan: 'Channel', ctx: Transaction, pcp: byte
|
||||
def make_htlc_tx_with_open_channel(*, chan: 'Channel', pcp: bytes, subject: 'HTLCOwner', ctn: int,
|
||||
htlc_direction: 'Direction', commit: Transaction, ctx_output_idx: int,
|
||||
htlc: 'UpdateAddHtlc', name: str = None) -> Tuple[bytes, PartialTransaction]:
|
||||
amount_msat, cltv_expiry, payment_hash = htlc.amount_msat, htlc.cltv_expiry, htlc.payment_hash
|
||||
amount_msat, cltv_abs, payment_hash = htlc.amount_msat, htlc.cltv_abs, htlc.payment_hash
|
||||
for_us = subject == LOCAL
|
||||
conf, other_conf = get_ordered_channel_configs(chan=chan, for_us=for_us)
|
||||
|
||||
@@ -864,14 +882,14 @@ def make_htlc_tx_with_open_channel(*, chan: 'Channel', pcp: bytes, subject: 'HTL
|
||||
remote_htlc_pubkey=other_htlc_pubkey,
|
||||
local_htlc_pubkey=htlc_pubkey,
|
||||
payment_hash=payment_hash,
|
||||
cltv_expiry=cltv_expiry)
|
||||
cltv_abs=cltv_abs)
|
||||
htlc_tx_inputs = make_htlc_tx_inputs(
|
||||
commit.txid(), ctx_output_idx,
|
||||
amount_msat=amount_msat,
|
||||
witness_script=preimage_script.hex())
|
||||
if is_htlc_success:
|
||||
cltv_expiry = 0
|
||||
htlc_tx = make_htlc_tx(cltv_expiry=cltv_expiry, inputs=htlc_tx_inputs, output=htlc_tx_output)
|
||||
cltv_abs = 0
|
||||
htlc_tx = make_htlc_tx(cltv_abs=cltv_abs, inputs=htlc_tx_inputs, output=htlc_tx_output)
|
||||
return witness_script_of_htlc_tx_output, htlc_tx
|
||||
|
||||
def make_funding_input(local_funding_pubkey: bytes, remote_funding_pubkey: bytes,
|
||||
@@ -1009,7 +1027,7 @@ def make_commitment(
|
||||
# the outputs are ordered in increasing cltv_expiry order."
|
||||
# so we sort by cltv_expiry now; and the later BIP69-sort is assumed to be *stable*
|
||||
htlcs = list(htlcs)
|
||||
htlcs.sort(key=lambda x: x.htlc.cltv_expiry)
|
||||
htlcs.sort(key=lambda x: x.htlc.cltv_abs)
|
||||
|
||||
htlc_outputs, c_outputs_filtered = make_commitment_outputs(
|
||||
fees_per_participant=fees_per_participant,
|
||||
@@ -1595,21 +1613,21 @@ NUM_MAX_EDGES_IN_PAYMENT_PATH = NUM_MAX_HOPS_IN_PAYMENT_PATH
|
||||
class UpdateAddHtlc:
|
||||
amount_msat = attr.ib(type=int, kw_only=True)
|
||||
payment_hash = attr.ib(type=bytes, kw_only=True, converter=hex_to_bytes, repr=lambda val: val.hex())
|
||||
cltv_expiry = attr.ib(type=int, kw_only=True)
|
||||
cltv_abs = attr.ib(type=int, kw_only=True)
|
||||
timestamp = attr.ib(type=int, kw_only=True)
|
||||
htlc_id = attr.ib(type=int, kw_only=True, default=None)
|
||||
|
||||
@stored_in('adds', tuple)
|
||||
def from_tuple(amount_msat, payment_hash, cltv_expiry, htlc_id, timestamp) -> 'UpdateAddHtlc':
|
||||
def from_tuple(amount_msat, payment_hash, cltv_abs, htlc_id, timestamp) -> 'UpdateAddHtlc':
|
||||
return UpdateAddHtlc(
|
||||
amount_msat=amount_msat,
|
||||
payment_hash=payment_hash,
|
||||
cltv_expiry=cltv_expiry,
|
||||
cltv_abs=cltv_abs,
|
||||
htlc_id=htlc_id,
|
||||
timestamp=timestamp)
|
||||
|
||||
def to_json(self):
|
||||
return (self.amount_msat, self.payment_hash, self.cltv_expiry, self.htlc_id, self.timestamp)
|
||||
return (self.amount_msat, self.payment_hash, self.cltv_abs, self.htlc_id, self.timestamp)
|
||||
|
||||
|
||||
class OnionFailureCodeMetaFlag(IntFlag):
|
||||
|
||||
@@ -510,8 +510,8 @@ class LNWalletWatcher(LNWatcher):
|
||||
prev_txid, prev_index = prevout.split(':')
|
||||
can_broadcast = True
|
||||
local_height = self.network.get_local_height()
|
||||
if sweep_info.cltv_expiry:
|
||||
wanted_height = sweep_info.cltv_expiry
|
||||
if sweep_info.cltv_abs:
|
||||
wanted_height = sweep_info.cltv_abs
|
||||
if wanted_height - local_height > 0:
|
||||
can_broadcast = False
|
||||
# self.logger.debug(f"pending redeem for {prevout}. waiting for {name}: CLTV ({local_height=}, {wanted_height=})")
|
||||
|
||||
+26
-26
@@ -61,7 +61,7 @@ from .lnutil import (Outpoint, LNPeerAddr,
|
||||
get_compressed_pubkey_from_bech32, extract_nodeid,
|
||||
PaymentFailure, split_host_port, ConnStringFormatError,
|
||||
generate_keypair, LnKeyFamily, LOCAL, REMOTE,
|
||||
MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE,
|
||||
MIN_FINAL_CLTV_DELTA_FOR_INVOICE,
|
||||
NUM_MAX_EDGES_IN_PAYMENT_PATH, SENT, RECEIVED, HTLCOwner,
|
||||
UpdateAddHtlc, Direction, LnFeatures, ShortChannelID,
|
||||
HtlcLog, derive_payment_secret_from_payment_preimage,
|
||||
@@ -662,7 +662,7 @@ class PaySession(Logger):
|
||||
initial_trampoline_fee_level: int,
|
||||
invoice_features: int,
|
||||
r_tags,
|
||||
min_cltv_expiry: int,
|
||||
min_final_cltv_delta: int, # delta for last edge (typically from invoice)
|
||||
amount_to_pay: int, # total payment amount final receiver will get
|
||||
invoice_pubkey: bytes,
|
||||
uses_trampoline: bool, # whether sender uses trampoline or gossip
|
||||
@@ -677,7 +677,7 @@ class PaySession(Logger):
|
||||
|
||||
self.invoice_features = LnFeatures(invoice_features)
|
||||
self.r_tags = r_tags
|
||||
self.min_cltv_expiry = min_cltv_expiry
|
||||
self.min_final_cltv_delta = min_final_cltv_delta
|
||||
self.amount_to_pay = amount_to_pay
|
||||
self.invoice_pubkey = invoice_pubkey
|
||||
|
||||
@@ -1387,7 +1387,7 @@ class LNWallet(LNWorker):
|
||||
) -> Tuple[bool, List[HtlcLog]]:
|
||||
|
||||
lnaddr = self._check_invoice(invoice, amount_msat=amount_msat)
|
||||
min_cltv_expiry = lnaddr.get_min_final_cltv_expiry()
|
||||
min_final_cltv_delta = lnaddr.get_min_final_cltv_delta()
|
||||
payment_hash = lnaddr.paymenthash
|
||||
key = payment_hash.hex()
|
||||
payment_secret = lnaddr.payment_secret
|
||||
@@ -1417,7 +1417,7 @@ class LNWallet(LNWorker):
|
||||
payment_hash=payment_hash,
|
||||
payment_secret=payment_secret,
|
||||
amount_to_pay=amount_to_pay,
|
||||
min_cltv_expiry=min_cltv_expiry,
|
||||
min_final_cltv_delta=min_final_cltv_delta,
|
||||
r_tags=r_tags,
|
||||
invoice_features=invoice_features,
|
||||
attempts=attempts,
|
||||
@@ -1447,7 +1447,7 @@ class LNWallet(LNWorker):
|
||||
payment_hash: bytes,
|
||||
payment_secret: bytes,
|
||||
amount_to_pay: int, # in msat
|
||||
min_cltv_expiry: int,
|
||||
min_final_cltv_delta: int,
|
||||
r_tags,
|
||||
invoice_features: int,
|
||||
attempts: int = None,
|
||||
@@ -1473,7 +1473,7 @@ class LNWallet(LNWorker):
|
||||
initial_trampoline_fee_level=self.config.INITIAL_TRAMPOLINE_FEE_LEVEL,
|
||||
invoice_features=invoice_features,
|
||||
r_tags=r_tags,
|
||||
min_cltv_expiry=min_cltv_expiry,
|
||||
min_final_cltv_delta=min_final_cltv_delta,
|
||||
amount_to_pay=amount_to_pay,
|
||||
invoice_pubkey=node_pubkey,
|
||||
uses_trampoline=self.uses_trampoline(),
|
||||
@@ -1502,7 +1502,7 @@ class LNWallet(LNWorker):
|
||||
await self.pay_to_route(
|
||||
paysession=paysession,
|
||||
sent_htlc_info=sent_htlc_info,
|
||||
min_cltv_expiry=cltv_delta,
|
||||
min_final_cltv_delta=cltv_delta,
|
||||
trampoline_onion=trampoline_onion,
|
||||
)
|
||||
# invoice_status is triggered in self.set_invoice_status when it actually changes.
|
||||
@@ -1559,7 +1559,7 @@ class LNWallet(LNWorker):
|
||||
self, *,
|
||||
paysession: PaySession,
|
||||
sent_htlc_info: SentHtlcInfo,
|
||||
min_cltv_expiry: int,
|
||||
min_final_cltv_delta: int,
|
||||
trampoline_onion: Optional[OnionPacket] = None,
|
||||
) -> None:
|
||||
"""Sends a single HTLC."""
|
||||
@@ -1578,7 +1578,7 @@ class LNWallet(LNWorker):
|
||||
amount_msat=shi.amount_msat,
|
||||
total_msat=shi.bucket_msat,
|
||||
payment_hash=paysession.payment_hash,
|
||||
min_final_cltv_expiry=min_cltv_expiry,
|
||||
min_final_cltv_delta=min_final_cltv_delta,
|
||||
payment_secret=shi.payment_secret_bucket,
|
||||
trampoline_onion=trampoline_onion)
|
||||
|
||||
@@ -1735,10 +1735,10 @@ class LNWallet(LNWorker):
|
||||
if addr.amount is None:
|
||||
raise InvoiceError(_("Missing amount"))
|
||||
# check cltv
|
||||
if addr.get_min_final_cltv_expiry() > lnutil.NBLOCK_CLTV_EXPIRY_TOO_FAR_INTO_FUTURE:
|
||||
if addr.get_min_final_cltv_delta() > lnutil.NBLOCK_CLTV_DELTA_TOO_FAR_INTO_FUTURE:
|
||||
raise InvoiceError("{}\n{}".format(
|
||||
_("Invoice wants us to risk locking funds for unreasonably long."),
|
||||
f"min_final_cltv_expiry: {addr.get_min_final_cltv_expiry()}"))
|
||||
f"min_final_cltv_delta: {addr.get_min_final_cltv_delta()}"))
|
||||
# check features
|
||||
addr.validate_and_compare_features(self.features)
|
||||
return addr
|
||||
@@ -1858,13 +1858,13 @@ class LNWallet(LNWorker):
|
||||
trampoline_onion = None
|
||||
per_trampoline_secret = paysession.payment_secret
|
||||
per_trampoline_amount_with_fees = amount_msat
|
||||
per_trampoline_cltv_delta = paysession.min_cltv_expiry
|
||||
per_trampoline_cltv_delta = paysession.min_final_cltv_delta
|
||||
per_trampoline_fees = 0
|
||||
else:
|
||||
trampoline_route, trampoline_onion, per_trampoline_amount_with_fees, per_trampoline_cltv_delta = create_trampoline_route_and_onion(
|
||||
amount_msat=per_trampoline_amount,
|
||||
total_msat=paysession.amount_to_pay,
|
||||
min_cltv_expiry=paysession.min_cltv_expiry,
|
||||
min_final_cltv_delta=paysession.min_final_cltv_delta,
|
||||
my_pubkey=self.node_keypair.pubkey,
|
||||
invoice_pubkey=paysession.invoice_pubkey,
|
||||
invoice_features=paysession.invoice_features,
|
||||
@@ -1896,7 +1896,7 @@ class LNWallet(LNWorker):
|
||||
short_channel_id=chan.short_channel_id,
|
||||
fee_base_msat=0,
|
||||
fee_proportional_millionths=0,
|
||||
cltv_expiry_delta=0,
|
||||
cltv_delta=0,
|
||||
node_features=trampoline_features)
|
||||
]
|
||||
self.logger.info(f'adding route {part_amount_msat} {delta_fee} {margin}')
|
||||
@@ -1925,7 +1925,7 @@ class LNWallet(LNWorker):
|
||||
self.create_route_for_payment,
|
||||
amount_msat=part_amount_msat,
|
||||
invoice_pubkey=paysession.invoice_pubkey,
|
||||
min_cltv_expiry=paysession.min_cltv_expiry,
|
||||
min_final_cltv_delta=paysession.min_final_cltv_delta,
|
||||
r_tags=paysession.r_tags,
|
||||
invoice_features=paysession.invoice_features,
|
||||
my_sending_channels=[channel] if is_multichan_mpp else my_active_channels,
|
||||
@@ -1942,7 +1942,7 @@ class LNWallet(LNWorker):
|
||||
trampoline_fee_level=None,
|
||||
trampoline_route=None,
|
||||
)
|
||||
routes.append((shi, paysession.min_cltv_expiry, fwd_trampoline_onion))
|
||||
routes.append((shi, paysession.min_final_cltv_delta, fwd_trampoline_onion))
|
||||
except NoPathFound:
|
||||
continue
|
||||
for route in routes:
|
||||
@@ -1955,7 +1955,7 @@ class LNWallet(LNWorker):
|
||||
self, *,
|
||||
amount_msat: int,
|
||||
invoice_pubkey: bytes,
|
||||
min_cltv_expiry: int,
|
||||
min_final_cltv_delta: int,
|
||||
r_tags,
|
||||
invoice_features: int,
|
||||
my_sending_channels: List[Channel],
|
||||
@@ -1978,7 +1978,7 @@ class LNWallet(LNWorker):
|
||||
self.logger.info(f'create_route: skipping alias {ShortChannelID(private_path[0][1])}')
|
||||
continue
|
||||
for end_node, edge_rest in zip(private_path_nodes, private_path_rest):
|
||||
short_channel_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta = edge_rest
|
||||
short_channel_id, fee_base_msat, fee_proportional_millionths, cltv_delta = edge_rest
|
||||
short_channel_id = ShortChannelID(short_channel_id)
|
||||
# if we have a routing policy for this edge in the db, that takes precedence,
|
||||
# as it is likely from a previous failure
|
||||
@@ -1989,7 +1989,7 @@ class LNWallet(LNWorker):
|
||||
if channel_policy:
|
||||
fee_base_msat = channel_policy.fee_base_msat
|
||||
fee_proportional_millionths = channel_policy.fee_proportional_millionths
|
||||
cltv_expiry_delta = channel_policy.cltv_expiry_delta
|
||||
cltv_delta = channel_policy.cltv_delta
|
||||
node_info = self.channel_db.get_node_info_for_node_id(node_id=end_node)
|
||||
route_edge = RouteEdge(
|
||||
start_node=start_node,
|
||||
@@ -1997,7 +1997,7 @@ class LNWallet(LNWorker):
|
||||
short_channel_id=short_channel_id,
|
||||
fee_base_msat=fee_base_msat,
|
||||
fee_proportional_millionths=fee_proportional_millionths,
|
||||
cltv_expiry_delta=cltv_expiry_delta,
|
||||
cltv_delta=cltv_delta,
|
||||
node_features=node_info.features if node_info else 0)
|
||||
private_route_edges[route_edge.short_channel_id] = route_edge
|
||||
start_node = end_node
|
||||
@@ -2015,7 +2015,7 @@ class LNWallet(LNWorker):
|
||||
if not route:
|
||||
raise NoPathFound()
|
||||
# test sanity
|
||||
if not is_route_sane_to_use(route, amount_msat, min_cltv_expiry):
|
||||
if not is_route_sane_to_use(route, amount_msat, min_final_cltv_delta):
|
||||
self.logger.info(f"rejecting insane route {route}")
|
||||
raise NoPathFound()
|
||||
assert len(route) > 0
|
||||
@@ -2059,7 +2059,7 @@ class LNWallet(LNWorker):
|
||||
amount=amount_btc,
|
||||
tags=[
|
||||
('d', message),
|
||||
('c', MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE),
|
||||
('c', MIN_FINAL_CLTV_DELTA_FOR_INVOICE),
|
||||
('x', expiry),
|
||||
('9', invoice_features),
|
||||
('f', fallback_address),
|
||||
@@ -2400,14 +2400,14 @@ class LNWallet(LNWorker):
|
||||
# (they will get the channel update from the onion error)
|
||||
# at least, that's the theory. https://github.com/lightningnetwork/lnd/issues/2066
|
||||
fee_base_msat = fee_proportional_millionths = 0
|
||||
cltv_expiry_delta = 1 # lnd won't even try with zero
|
||||
cltv_delta = 1 # lnd won't even try with zero
|
||||
missing_info = True
|
||||
if channel_info:
|
||||
policy = get_mychannel_policy(channel_info.short_channel_id, chan.node_id, scid_to_my_channels)
|
||||
if policy:
|
||||
fee_base_msat = policy.fee_base_msat
|
||||
fee_proportional_millionths = policy.fee_proportional_millionths
|
||||
cltv_expiry_delta = policy.cltv_expiry_delta
|
||||
cltv_delta = policy.cltv_delta
|
||||
missing_info = False
|
||||
if missing_info:
|
||||
self.logger.info(
|
||||
@@ -2418,7 +2418,7 @@ class LNWallet(LNWorker):
|
||||
alias_or_scid,
|
||||
fee_base_msat,
|
||||
fee_proportional_millionths,
|
||||
cltv_expiry_delta)]))
|
||||
cltv_delta)]))
|
||||
return routing_hints
|
||||
|
||||
def delete_payment_info(self, payment_hash_hex: str):
|
||||
|
||||
@@ -142,19 +142,19 @@ class TestBolt11(ElectrumTestCase):
|
||||
def test_min_final_cltv_expiry_decoding(self):
|
||||
lnaddr = lndecode("lnsb500u1pdsgyf3pp5nmrqejdsdgs4n9ukgxcp2kcq265yhrxd4k5dyue58rxtp5y83s3qsp5qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsdqqcqzys9qypqsqp2h6a5xeytuc3fad2ed4gxvhd593lwjdna3dxsyeem0qkzjx6guk44jend0xq4zzvp6f3fy07wnmxezazzsxgmvqee8shxjuqu2eu0qpnvc95x",
|
||||
net=constants.BitcoinSimnet)
|
||||
self.assertEqual(144, lnaddr.get_min_final_cltv_expiry())
|
||||
self.assertEqual(144, lnaddr.get_min_final_cltv_delta())
|
||||
|
||||
lnaddr = lndecode("lntb15u1p0m6lzupp5zqjthgvaad9mewmdjuehwddyze9d8zyxcc43zhaddeegt37sndgsdq4xysyymr0vd4kzcmrd9hx7cqp7xqrrss9qy9qsqsp5vlhcs24hwm747w8f3uau2tlrdkvjaglffnsstwyamj84cxuhrn2s8tut3jqumepu42azyyjpgqa4w9w03204zp9h4clk499y2umstl6s29hqyj8vv4as6zt5567ux7l3f66m8pjhk65zjaq2esezk7ll2kcpljewkg",
|
||||
net=constants.BitcoinTestnet)
|
||||
self.assertEqual(30, lnaddr.get_min_final_cltv_expiry())
|
||||
self.assertEqual(30, lnaddr.get_min_final_cltv_delta())
|
||||
|
||||
def test_min_final_cltv_expiry_roundtrip(self):
|
||||
for cltv in (1, 15, 16, 31, 32, 33, 150, 511, 512, 513, 1023, 1024, 1025):
|
||||
lnaddr = LnAddr(
|
||||
paymenthash=RHASH, payment_secret=b"\x01"*32, amount=Decimal('0.001'), tags=[('d', '1 cup coffee'), ('x', 60), ('c', cltv), ('9', 33282)])
|
||||
self.assertEqual(cltv, lnaddr.get_min_final_cltv_expiry())
|
||||
self.assertEqual(cltv, lnaddr.get_min_final_cltv_delta())
|
||||
invoice = lnencode(lnaddr, PRIVKEY)
|
||||
self.assertEqual(cltv, lndecode(invoice).get_min_final_cltv_expiry())
|
||||
self.assertEqual(cltv, lndecode(invoice).get_min_final_cltv_delta())
|
||||
|
||||
def test_features(self):
|
||||
lnaddr = lndecode("lnbc25m1pvjluezpp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygsdq5vdhkven9v5sxyetpdees9qypqsztrz5v3jfnxskfv7g8chmyzyrfhf2vupcavuq5rce96kyt6g0zh337h206awccwp335zarqrud4wccgdn39vur44d8um4hmgv06aj0sgpdrv73z")
|
||||
|
||||
@@ -33,7 +33,7 @@ from electrum import lnpeer
|
||||
from electrum import lnchannel
|
||||
from electrum import lnutil
|
||||
from electrum import bip32 as bip32_utils
|
||||
from electrum.lnutil import SENT, LOCAL, REMOTE, RECEIVED
|
||||
from electrum.lnutil import SENT, LOCAL, REMOTE, RECEIVED, UpdateAddHtlc
|
||||
from electrum.logging import console_stderr_handler
|
||||
from electrum.lnchannel import ChannelState
|
||||
from electrum.json_db import StoredDict
|
||||
@@ -240,7 +240,7 @@ class TestChannel(ElectrumTestCase):
|
||||
self.htlc_dict = {
|
||||
'payment_hash' : paymentHash,
|
||||
'amount_msat' : one_bitcoin_in_msat,
|
||||
'cltv_expiry' : 5,
|
||||
'cltv_abs' : 5,
|
||||
'timestamp' : 0,
|
||||
}
|
||||
|
||||
@@ -648,15 +648,15 @@ class TestAvailableToSpend(ElectrumTestCase):
|
||||
|
||||
paymentPreimage = b"\x01" * 32
|
||||
paymentHash = bitcoin.sha256(paymentPreimage)
|
||||
htlc_dict = {
|
||||
'payment_hash' : paymentHash,
|
||||
'amount_msat' : one_bitcoin_in_msat * 41 // 10,
|
||||
'cltv_expiry' : 5,
|
||||
'timestamp' : 0,
|
||||
}
|
||||
htlc = UpdateAddHtlc(
|
||||
payment_hash=paymentHash,
|
||||
amount_msat=one_bitcoin_in_msat * 41 // 10,
|
||||
cltv_abs=5,
|
||||
timestamp=0,
|
||||
)
|
||||
|
||||
alice_idx = alice_channel.add_htlc(htlc_dict).htlc_id
|
||||
bob_idx = bob_channel.receive_htlc(htlc_dict).htlc_id
|
||||
alice_idx = alice_channel.add_htlc(htlc).htlc_id
|
||||
bob_idx = bob_channel.receive_htlc(htlc).htlc_id
|
||||
self.assertEqual(89984088000, alice_channel.available_to_spend(LOCAL))
|
||||
self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL))
|
||||
|
||||
@@ -672,20 +672,20 @@ class TestAvailableToSpend(ElectrumTestCase):
|
||||
# available.
|
||||
# We try adding an HTLC of value 1 BTC, which should fail because the
|
||||
# balance is unavailable.
|
||||
htlc_dict = {
|
||||
'payment_hash' : paymentHash,
|
||||
'amount_msat' : one_bitcoin_in_msat,
|
||||
'cltv_expiry' : 5,
|
||||
'timestamp' : 0,
|
||||
}
|
||||
htlc = UpdateAddHtlc(
|
||||
payment_hash=paymentHash,
|
||||
amount_msat=one_bitcoin_in_msat,
|
||||
cltv_abs=5,
|
||||
timestamp=0,
|
||||
)
|
||||
with self.assertRaises(lnutil.PaymentFailure):
|
||||
alice_channel.add_htlc(htlc_dict)
|
||||
alice_channel.add_htlc(htlc)
|
||||
# Now do a state transition, which will ACK the FailHTLC, making Alice
|
||||
# able to add the new HTLC.
|
||||
force_state_transition(alice_channel, bob_channel)
|
||||
self.assertEqual(499986152000, alice_channel.available_to_spend(LOCAL))
|
||||
self.assertEqual(500000000000, bob_channel.available_to_spend(LOCAL))
|
||||
alice_channel.add_htlc(htlc_dict)
|
||||
alice_channel.add_htlc(htlc)
|
||||
|
||||
|
||||
class TestChanReserve(ElectrumTestCase):
|
||||
@@ -722,7 +722,7 @@ class TestChanReserve(ElectrumTestCase):
|
||||
htlc_dict = {
|
||||
'payment_hash' : paymentHash,
|
||||
'amount_msat' : int(.5 * one_bitcoin_in_msat),
|
||||
'cltv_expiry' : 5,
|
||||
'cltv_abs' : 5,
|
||||
'timestamp' : 0,
|
||||
}
|
||||
self.alice_channel.add_htlc(htlc_dict)
|
||||
@@ -761,7 +761,7 @@ class TestChanReserve(ElectrumTestCase):
|
||||
htlc_dict = {
|
||||
'payment_hash' : paymentHash,
|
||||
'amount_msat' : int(3.5 * one_bitcoin_in_msat),
|
||||
'cltv_expiry' : 5,
|
||||
'cltv_abs' : 5,
|
||||
}
|
||||
self.alice_channel.add_htlc(htlc_dict)
|
||||
self.bob_channel.receive_htlc(htlc_dict)
|
||||
@@ -785,7 +785,7 @@ class TestChanReserve(ElectrumTestCase):
|
||||
htlc_dict = {
|
||||
'payment_hash' : paymentHash,
|
||||
'amount_msat' : int(2 * one_bitcoin_in_msat),
|
||||
'cltv_expiry' : 5,
|
||||
'cltv_abs' : 5,
|
||||
'timestamp' : 0,
|
||||
}
|
||||
alice_idx = self.alice_channel.add_htlc(htlc_dict).htlc_id
|
||||
@@ -830,7 +830,7 @@ class TestDust(ElectrumTestCase):
|
||||
htlc = {
|
||||
'payment_hash' : paymentHash,
|
||||
'amount_msat' : 1000 * htlcAmt,
|
||||
'cltv_expiry' : 5, # also in create_test_channels
|
||||
'cltv_abs' : 5, # also in create_test_channels
|
||||
'timestamp' : 0,
|
||||
}
|
||||
|
||||
|
||||
@@ -239,7 +239,7 @@ class MockLNWallet(Logger, EventListener, NetworkRetryManager[LNPeerAddr]):
|
||||
initial_trampoline_fee_level=0,
|
||||
invoice_features=decoded_invoice.get_features(),
|
||||
r_tags=decoded_invoice.get_routing_info('r'),
|
||||
min_cltv_expiry=decoded_invoice.get_min_final_cltv_expiry(),
|
||||
min_final_cltv_delta=decoded_invoice.get_min_final_cltv_delta(),
|
||||
amount_to_pay=amount_msat,
|
||||
invoice_pubkey=decoded_invoice.pubkey.serialize(),
|
||||
uses_trampoline=False,
|
||||
@@ -455,7 +455,7 @@ class TestPeer(ElectrumTestCase):
|
||||
payment_preimage: bytes = None,
|
||||
payment_hash: bytes = None,
|
||||
invoice_features: LnFeatures = None,
|
||||
min_final_cltv_expiry_delta: int = None,
|
||||
min_final_cltv_delta: int = None,
|
||||
) -> Tuple[LnAddr, str]:
|
||||
amount_btc = amount_msat/Decimal(COIN*1000)
|
||||
if payment_preimage is None and not payment_hash:
|
||||
@@ -477,13 +477,13 @@ class TestPeer(ElectrumTestCase):
|
||||
payment_secret = w2.get_payment_secret(payment_hash)
|
||||
else:
|
||||
payment_secret = None
|
||||
if min_final_cltv_expiry_delta is None:
|
||||
min_final_cltv_expiry_delta = lnutil.MIN_FINAL_CLTV_EXPIRY_FOR_INVOICE
|
||||
if min_final_cltv_delta is None:
|
||||
min_final_cltv_delta = lnutil.MIN_FINAL_CLTV_DELTA_FOR_INVOICE
|
||||
lnaddr1 = LnAddr(
|
||||
paymenthash=payment_hash,
|
||||
amount=amount_btc,
|
||||
tags=[
|
||||
('c', min_final_cltv_expiry_delta),
|
||||
('c', min_final_cltv_delta),
|
||||
('d', 'coffee'),
|
||||
('9', invoice_features),
|
||||
] + routing_hints,
|
||||
@@ -574,13 +574,13 @@ class TestPeerDirect(TestPeer):
|
||||
|
||||
@staticmethod
|
||||
def _send_fake_htlc(peer: Peer, chan: Channel) -> UpdateAddHtlc:
|
||||
htlc = UpdateAddHtlc(amount_msat=10000, payment_hash=os.urandom(32), cltv_expiry=999, timestamp=1)
|
||||
htlc = UpdateAddHtlc(amount_msat=10000, payment_hash=os.urandom(32), cltv_abs=999, timestamp=1)
|
||||
htlc = chan.add_htlc(htlc)
|
||||
peer.send_message(
|
||||
"update_add_htlc",
|
||||
channel_id=chan.channel_id,
|
||||
id=htlc.htlc_id,
|
||||
cltv_expiry=htlc.cltv_expiry,
|
||||
cltv_expiry=htlc.cltv_abs,
|
||||
amount_msat=htlc.amount_msat,
|
||||
payment_hash=htlc.payment_hash,
|
||||
onion_routing_packet=1366 * b"0",
|
||||
@@ -798,7 +798,7 @@ class TestPeerDirect(TestPeer):
|
||||
with self.assertRaises(lnutil.IncompatibleOrInsaneFeatures):
|
||||
result, log = await w1.pay_invoice(pay_req)
|
||||
# too large CLTV
|
||||
lnaddr, pay_req = self.prepare_invoice(w2, min_final_cltv_expiry_delta=10**6)
|
||||
lnaddr, pay_req = self.prepare_invoice(w2, min_final_cltv_delta=10**6)
|
||||
with self.assertRaises(InvoiceError):
|
||||
result, log = await w1.pay_invoice(pay_req)
|
||||
raise SuccessfulTest()
|
||||
@@ -848,7 +848,7 @@ class TestPeerDirect(TestPeer):
|
||||
await w1.pay_to_route(
|
||||
sent_htlc_info=shi1,
|
||||
paysession=paysession1,
|
||||
min_cltv_expiry=lnaddr2.get_min_final_cltv_expiry(),
|
||||
min_final_cltv_delta=lnaddr2.get_min_final_cltv_delta(),
|
||||
)
|
||||
p1.maybe_send_commitment = _maybe_send_commitment1
|
||||
# bob sends htlc BUT NOT COMMITMENT_SIGNED
|
||||
@@ -868,7 +868,7 @@ class TestPeerDirect(TestPeer):
|
||||
await w2.pay_to_route(
|
||||
sent_htlc_info=shi2,
|
||||
paysession=paysession2,
|
||||
min_cltv_expiry=lnaddr1.get_min_final_cltv_expiry(),
|
||||
min_final_cltv_delta=lnaddr1.get_min_final_cltv_delta(),
|
||||
)
|
||||
p2.maybe_send_commitment = _maybe_send_commitment2
|
||||
# sleep a bit so that they both receive msgs sent so far
|
||||
@@ -941,7 +941,7 @@ class TestPeerDirect(TestPeer):
|
||||
amount_msat=1000,
|
||||
total_msat=lnaddr1.get_amount_msat(),
|
||||
payment_hash=lnaddr1.paymenthash,
|
||||
min_final_cltv_expiry=lnaddr1.get_min_final_cltv_expiry(),
|
||||
min_final_cltv_delta=lnaddr1.get_min_final_cltv_delta(),
|
||||
payment_secret=lnaddr1.payment_secret,
|
||||
)
|
||||
p1.pay(
|
||||
@@ -950,7 +950,7 @@ class TestPeerDirect(TestPeer):
|
||||
amount_msat=lnaddr1.get_amount_msat() - 1000,
|
||||
total_msat=lnaddr1.get_amount_msat(),
|
||||
payment_hash=lnaddr2.paymenthash,
|
||||
min_final_cltv_expiry=lnaddr1.get_min_final_cltv_expiry(),
|
||||
min_final_cltv_delta=lnaddr1.get_min_final_cltv_delta(),
|
||||
payment_secret=lnaddr1.payment_secret,
|
||||
)
|
||||
|
||||
@@ -1017,7 +1017,7 @@ class TestPeerDirect(TestPeer):
|
||||
amount_msat=1000,
|
||||
total_msat=2000,
|
||||
payment_hash=lnaddr1.paymenthash,
|
||||
min_final_cltv_expiry=lnaddr1.get_min_final_cltv_expiry(),
|
||||
min_final_cltv_delta=lnaddr1.get_min_final_cltv_delta(),
|
||||
payment_secret=lnaddr1.payment_secret,
|
||||
)
|
||||
p1.pay(
|
||||
@@ -1026,7 +1026,7 @@ class TestPeerDirect(TestPeer):
|
||||
amount_msat=1000,
|
||||
total_msat=lnaddr1.get_amount_msat(),
|
||||
payment_hash=lnaddr1.paymenthash,
|
||||
min_final_cltv_expiry=lnaddr1.get_min_final_cltv_expiry(),
|
||||
min_final_cltv_delta=lnaddr1.get_min_final_cltv_delta(),
|
||||
payment_secret=lnaddr1.payment_secret,
|
||||
)
|
||||
|
||||
@@ -1121,7 +1121,7 @@ class TestPeerDirect(TestPeer):
|
||||
amount_msat=lnaddr.get_amount_msat(),
|
||||
total_msat=lnaddr.get_amount_msat(),
|
||||
payment_hash=lnaddr.paymenthash,
|
||||
min_final_cltv_expiry=lnaddr.get_min_final_cltv_expiry(),
|
||||
min_final_cltv_delta=lnaddr.get_min_final_cltv_delta(),
|
||||
payment_secret=lnaddr.payment_secret)
|
||||
# alice closes
|
||||
await p1.close_channel(alice_channel.channel_id)
|
||||
@@ -1264,7 +1264,7 @@ class TestPeerDirect(TestPeer):
|
||||
pay = w1.pay_to_route(
|
||||
sent_htlc_info=shi,
|
||||
paysession=paysession,
|
||||
min_cltv_expiry=lnaddr.get_min_final_cltv_expiry(),
|
||||
min_final_cltv_delta=lnaddr.get_min_final_cltv_delta(),
|
||||
)
|
||||
await asyncio.gather(pay, p1._message_loop(), p2._message_loop(), p1.htlc_switch(), p2.htlc_switch())
|
||||
with self.assertRaises(PaymentFailure):
|
||||
|
||||
@@ -482,26 +482,31 @@ class TestLNUtil(ElectrumTestCase):
|
||||
htlc_cltv_timeout = {}
|
||||
htlc_payment_preimage = {}
|
||||
htlc = {}
|
||||
htlc_pubkeys = {
|
||||
"revocation_pubkey": local_revocation_pubkey,
|
||||
"remote_htlcpubkey": remote_htlcpubkey,
|
||||
"local_htlcpubkey": local_htlcpubkey,
|
||||
}
|
||||
|
||||
htlc_cltv_timeout[2] = 502
|
||||
htlc_payment_preimage[2] = b"\x02" * 32
|
||||
htlc[2] = make_offered_htlc(local_revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, bitcoin.sha256(htlc_payment_preimage[2]))
|
||||
htlc[2] = make_offered_htlc(**htlc_pubkeys, payment_hash=bitcoin.sha256(htlc_payment_preimage[2]))
|
||||
|
||||
htlc_cltv_timeout[3] = 503
|
||||
htlc_payment_preimage[3] = b"\x03" * 32
|
||||
htlc[3] = make_offered_htlc(local_revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, bitcoin.sha256(htlc_payment_preimage[3]))
|
||||
htlc[3] = make_offered_htlc(**htlc_pubkeys, payment_hash=bitcoin.sha256(htlc_payment_preimage[3]))
|
||||
|
||||
htlc_cltv_timeout[0] = 500
|
||||
htlc_payment_preimage[0] = b"\x00" * 32
|
||||
htlc[0] = make_received_htlc(local_revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, bitcoin.sha256(htlc_payment_preimage[0]), htlc_cltv_timeout[0])
|
||||
htlc[0] = make_received_htlc(**htlc_pubkeys, payment_hash=bitcoin.sha256(htlc_payment_preimage[0]), cltv_abs=htlc_cltv_timeout[0])
|
||||
|
||||
htlc_cltv_timeout[1] = 501
|
||||
htlc_payment_preimage[1] = b"\x01" * 32
|
||||
htlc[1] = make_received_htlc(local_revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, bitcoin.sha256(htlc_payment_preimage[1]), htlc_cltv_timeout[1])
|
||||
htlc[1] = make_received_htlc(**htlc_pubkeys, payment_hash=bitcoin.sha256(htlc_payment_preimage[1]), cltv_abs=htlc_cltv_timeout[1])
|
||||
|
||||
htlc_cltv_timeout[4] = 504
|
||||
htlc_payment_preimage[4] = b"\x04" * 32
|
||||
htlc[4] = make_received_htlc(local_revocation_pubkey, remote_htlcpubkey, local_htlcpubkey, bitcoin.sha256(htlc_payment_preimage[4]), htlc_cltv_timeout[4])
|
||||
htlc[4] = make_received_htlc(**htlc_pubkeys, payment_hash=bitcoin.sha256(htlc_payment_preimage[4]), cltv_abs=htlc_cltv_timeout[4])
|
||||
|
||||
remote_signature = "304402204fd4928835db1ccdfc40f5c78ce9bd65249b16348df81f0c44328dcdefc97d630220194d3869c38bc732dd87d13d2958015e2fc16829e74cd4377f84d215c0b70606"
|
||||
output_commit_tx = "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8007e80300000000000022002052bfef0479d7b293c27e0f1eb294bea154c63a3294ef092c19af51409bce0e2ad007000000000000220020403d394747cae42e98ff01734ad5c08f82ba123d3d9a620abda88989651e2ab5d007000000000000220020748eba944fedc8827f6b06bc44678f93c0f9e6078b35c6331ed31e75f8ce0c2db80b000000000000220020c20b5d1f8584fd90443e7b7b720136174fa4b9333c261d04dbbd012635c0f419a00f0000000000002200208c48d15160397c9731df9bc3b236656efb6665fbfe92b4a6878e88a499f741c4c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de843110e0a06a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e04004730440220275b0c325a5e9355650dc30c0eccfbc7efb23987c24b556b9dfdd40effca18d202206caceb2c067836c51f296740c7ae807ffcbfbf1dd3a0d56b6de9a5b247985f060147304402204fd4928835db1ccdfc40f5c78ce9bd65249b16348df81f0c44328dcdefc97d630220194d3869c38bc732dd87d13d2958015e2fc16829e74cd4377f84d215c0b7060601475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220"
|
||||
@@ -512,7 +517,7 @@ class TestLNUtil(ElectrumTestCase):
|
||||
(1, 2000 * 1000),
|
||||
(3, 3000 * 1000),
|
||||
(4, 4000 * 1000)]:
|
||||
htlc_obj[num] = UpdateAddHtlc(amount_msat=msat, payment_hash=bitcoin.sha256(htlc_payment_preimage[num]), cltv_expiry=0, htlc_id=None, timestamp=0)
|
||||
htlc_obj[num] = UpdateAddHtlc(amount_msat=msat, payment_hash=bitcoin.sha256(htlc_payment_preimage[num]), cltv_abs=0, htlc_id=None, timestamp=0)
|
||||
htlcs = [ScriptHtlc(htlc[x], htlc_obj[x]) for x in range(5)]
|
||||
|
||||
our_commit_tx = make_commitment(
|
||||
@@ -567,7 +572,7 @@ class TestLNUtil(ElectrumTestCase):
|
||||
local_feerate_per_kw,
|
||||
our_commit_tx))
|
||||
|
||||
def htlc_tx(self, htlc, htlc_output_index, amount_msat, htlc_payment_preimage, remote_htlc_sig, success, cltv_timeout, local_feerate_per_kw, our_commit_tx):
|
||||
def htlc_tx(self, htlc, htlc_output_index, amount_msat, htlc_payment_preimage, remote_htlc_sig, success, cltv_abs, local_feerate_per_kw, our_commit_tx):
|
||||
_script, our_htlc_tx_output = make_htlc_tx_output(
|
||||
amount_msat=amount_msat,
|
||||
local_feerate=local_feerate_per_kw,
|
||||
@@ -581,7 +586,7 @@ class TestLNUtil(ElectrumTestCase):
|
||||
amount_msat=amount_msat,
|
||||
witness_script=htlc.hex())
|
||||
our_htlc_tx = make_htlc_tx(
|
||||
cltv_expiry=cltv_timeout,
|
||||
cltv_abs=cltv_abs,
|
||||
inputs=our_htlc_tx_inputs,
|
||||
output=our_htlc_tx_output)
|
||||
|
||||
|
||||
+18
-18
@@ -6,7 +6,7 @@ from typing import Mapping, DefaultDict, Tuple, Optional, Dict, List, Iterable,
|
||||
|
||||
from .lnutil import LnFeatures
|
||||
from .lnonion import calc_hops_data_for_payment, new_onion_packet
|
||||
from .lnrouter import RouteEdge, TrampolineEdge, LNPaymentRoute, is_route_sane_to_use
|
||||
from .lnrouter import RouteEdge, TrampolineEdge, LNPaymentRoute, is_route_sane_to_use, LNPaymentTRoute
|
||||
from .lnutil import NoPathFound, LNPeerAddr
|
||||
from . import constants
|
||||
from .logging import get_logger
|
||||
@@ -183,7 +183,7 @@ def _extend_trampoline_route(
|
||||
end_node=end_node,
|
||||
fee_base_msat=policy['fee_base_msat'] if pay_fees else 0,
|
||||
fee_proportional_millionths=policy['fee_proportional_millionths'] if pay_fees else 0,
|
||||
cltv_expiry_delta=policy['cltv_expiry_delta'] if pay_fees else 0,
|
||||
cltv_delta=policy['cltv_expiry_delta'] if pay_fees else 0,
|
||||
node_features=trampoline_features))
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ def _choose_second_trampoline(
|
||||
def create_trampoline_route(
|
||||
*,
|
||||
amount_msat: int,
|
||||
min_cltv_expiry: int,
|
||||
min_final_cltv_delta: int,
|
||||
invoice_pubkey: bytes,
|
||||
invoice_features: int,
|
||||
my_pubkey: bytes,
|
||||
@@ -217,7 +217,7 @@ def create_trampoline_route(
|
||||
trampoline_fee_level: int,
|
||||
use_two_trampolines: bool,
|
||||
failed_routes: Iterable[Sequence[str]],
|
||||
) -> LNPaymentRoute:
|
||||
) -> LNPaymentTRoute:
|
||||
# we decide whether to convert to a legacy payment
|
||||
is_legacy, invoice_trampolines = is_legacy_relay(invoice_features, r_tags)
|
||||
|
||||
@@ -262,25 +262,25 @@ def create_trampoline_route(
|
||||
# check that we can pay amount and fees
|
||||
for edge in route[::-1]:
|
||||
amount_msat += edge.fee_for_edge(amount_msat)
|
||||
if not is_route_sane_to_use(route, amount_msat, min_cltv_expiry):
|
||||
if not is_route_sane_to_use(route, amount_msat, min_final_cltv_delta):
|
||||
raise NoPathFound("We cannot afford to pay the fees.")
|
||||
return route
|
||||
|
||||
|
||||
def create_trampoline_onion(
|
||||
*,
|
||||
route,
|
||||
amount_msat,
|
||||
final_cltv,
|
||||
route: LNPaymentTRoute,
|
||||
amount_msat: int,
|
||||
final_cltv_abs: int,
|
||||
total_msat: int,
|
||||
payment_hash: bytes,
|
||||
payment_secret: bytes,
|
||||
):
|
||||
# all edges are trampoline
|
||||
hops_data, amount_msat, cltv = calc_hops_data_for_payment(
|
||||
hops_data, amount_msat, cltv_abs = calc_hops_data_for_payment(
|
||||
route,
|
||||
amount_msat,
|
||||
final_cltv,
|
||||
final_cltv_abs=final_cltv_abs,
|
||||
total_msat=total_msat,
|
||||
payment_secret=payment_secret)
|
||||
# detect trampoline hops.
|
||||
@@ -313,14 +313,14 @@ def create_trampoline_onion(
|
||||
trampoline_onion = new_onion_packet(payment_path_pubkeys, trampoline_session_key, hops_data, associated_data=payment_hash, trampoline=True)
|
||||
trampoline_onion._debug_hops_data = hops_data
|
||||
trampoline_onion._debug_route = route
|
||||
return trampoline_onion, amount_msat, cltv
|
||||
return trampoline_onion, amount_msat, cltv_abs
|
||||
|
||||
|
||||
def create_trampoline_route_and_onion(
|
||||
*,
|
||||
amount_msat,
|
||||
total_msat,
|
||||
min_cltv_expiry,
|
||||
min_final_cltv_delta: int,
|
||||
invoice_pubkey,
|
||||
invoice_features,
|
||||
my_pubkey: bytes,
|
||||
@@ -335,7 +335,7 @@ def create_trampoline_route_and_onion(
|
||||
# create route for the trampoline_onion
|
||||
trampoline_route = create_trampoline_route(
|
||||
amount_msat=amount_msat,
|
||||
min_cltv_expiry=min_cltv_expiry,
|
||||
min_final_cltv_delta=min_final_cltv_delta,
|
||||
my_pubkey=my_pubkey,
|
||||
invoice_pubkey=invoice_pubkey,
|
||||
invoice_features=invoice_features,
|
||||
@@ -345,16 +345,16 @@ def create_trampoline_route_and_onion(
|
||||
use_two_trampolines=use_two_trampolines,
|
||||
failed_routes=failed_routes)
|
||||
# compute onion and fees
|
||||
final_cltv = local_height + min_cltv_expiry
|
||||
trampoline_onion, amount_with_fees, bucket_cltv = create_trampoline_onion(
|
||||
final_cltv_abs = local_height + min_final_cltv_delta
|
||||
trampoline_onion, amount_with_fees, bucket_cltv_abs = create_trampoline_onion(
|
||||
route=trampoline_route,
|
||||
amount_msat=amount_msat,
|
||||
final_cltv=final_cltv,
|
||||
final_cltv_abs=final_cltv_abs,
|
||||
total_msat=total_msat,
|
||||
payment_hash=payment_hash,
|
||||
payment_secret=payment_secret)
|
||||
bucket_cltv_delta = bucket_cltv - local_height
|
||||
bucket_cltv_delta += trampoline_route[0].cltv_expiry_delta
|
||||
bucket_cltv_delta = bucket_cltv_abs - local_height
|
||||
bucket_cltv_delta += trampoline_route[0].cltv_delta
|
||||
# trampoline fee for this very trampoline
|
||||
trampoline_fee = trampoline_route[0].fee_for_edge(amount_with_fees)
|
||||
amount_with_fees += trampoline_fee
|
||||
|
||||
Reference in New Issue
Block a user