|
|
|
@ -2021,23 +2021,23 @@ class Peer(Logger): |
|
|
|
processed_onion: ProcessedOnionPacket, |
|
|
|
processed_onion: ProcessedOnionPacket, |
|
|
|
onion_packet_bytes: bytes, |
|
|
|
onion_packet_bytes: bytes, |
|
|
|
already_forwarded: bool = False, |
|
|
|
already_forwarded: bool = False, |
|
|
|
) -> Tuple[Optional[str], Optional[bytes], Optional[Callable[[], Awaitable[Optional[str]]]]]: |
|
|
|
) -> Tuple[Optional[str], Optional[Tuple[str, Callable[[], Awaitable[Optional[str]]]]]]: |
|
|
|
""" |
|
|
|
""" |
|
|
|
Decide what to do with an HTLC: return preimage if it can be fulfilled, forwarding callback if it can be forwarded. |
|
|
|
Decide what to do with an HTLC: return preimage if it can be fulfilled, forwarding callback if it can be forwarded. |
|
|
|
Return (payment_key, preimage, callback) with at most a single element of the last two not None |
|
|
|
Return (preimage, (payment_key, callback)) with at most a single element not None. |
|
|
|
Side effect: populates lnworker.received_mpp (which is not persisted, needs to be re-populated after restart) |
|
|
|
Side effect: populates lnworker.received_mpp (which is not persisted, needs to be re-populated after restart) |
|
|
|
""" |
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
if not processed_onion.are_we_final: |
|
|
|
if not processed_onion.are_we_final: |
|
|
|
if not self.lnworker.enable_htlc_forwarding: |
|
|
|
if not self.lnworker.enable_htlc_forwarding: |
|
|
|
return None, None, None |
|
|
|
return None, None |
|
|
|
# use the htlc key if we are forwarding |
|
|
|
# use the htlc key if we are forwarding |
|
|
|
payment_key = serialize_htlc_key(chan.get_scid_or_local_alias(), htlc.htlc_id) |
|
|
|
payment_key = serialize_htlc_key(chan.get_scid_or_local_alias(), htlc.htlc_id) |
|
|
|
callback = lambda: self.maybe_forward_htlc( |
|
|
|
callback = lambda: self.maybe_forward_htlc( |
|
|
|
incoming_chan=chan, |
|
|
|
incoming_chan=chan, |
|
|
|
htlc=htlc, |
|
|
|
htlc=htlc, |
|
|
|
processed_onion=processed_onion) |
|
|
|
processed_onion=processed_onion) |
|
|
|
return payment_key, None, callback |
|
|
|
return None, (payment_key, callback) |
|
|
|
|
|
|
|
|
|
|
|
def log_fail_reason(reason: str): |
|
|
|
def log_fail_reason(reason: str): |
|
|
|
self.logger.info( |
|
|
|
self.logger.info( |
|
|
|
@ -2108,7 +2108,7 @@ class Peer(Logger): |
|
|
|
expected_msat=total_msat, |
|
|
|
expected_msat=total_msat, |
|
|
|
) |
|
|
|
) |
|
|
|
if mpp_resolution == RecvMPPResolution.WAITING: |
|
|
|
if mpp_resolution == RecvMPPResolution.WAITING: |
|
|
|
return payment_key, None, None |
|
|
|
return None, None |
|
|
|
elif mpp_resolution == RecvMPPResolution.EXPIRED: |
|
|
|
elif mpp_resolution == RecvMPPResolution.EXPIRED: |
|
|
|
log_fail_reason(f"MPP_TIMEOUT") |
|
|
|
log_fail_reason(f"MPP_TIMEOUT") |
|
|
|
raise OnionRoutingFailure(code=OnionFailureCode.MPP_TIMEOUT, data=b'') |
|
|
|
raise OnionRoutingFailure(code=OnionFailureCode.MPP_TIMEOUT, data=b'') |
|
|
|
@ -2125,7 +2125,7 @@ class Peer(Logger): |
|
|
|
log_fail_reason(f"htlc.cltv_abs is unreasonably close") |
|
|
|
log_fail_reason(f"htlc.cltv_abs is unreasonably close") |
|
|
|
raise exc_incorrect_or_unknown_pd |
|
|
|
raise exc_incorrect_or_unknown_pd |
|
|
|
else: |
|
|
|
else: |
|
|
|
return payment_key, None, None |
|
|
|
return None, None |
|
|
|
|
|
|
|
|
|
|
|
# detect callback |
|
|
|
# detect callback |
|
|
|
# if there is a trampoline_onion, maybe_fulfill_htlc will be called again |
|
|
|
# if there is a trampoline_onion, maybe_fulfill_htlc will be called again |
|
|
|
@ -2140,6 +2140,7 @@ class Peer(Logger): |
|
|
|
is_trampoline=True) |
|
|
|
is_trampoline=True) |
|
|
|
if trampoline_onion.are_we_final: |
|
|
|
if trampoline_onion.are_we_final: |
|
|
|
# trampoline- we are final recipient of HTLC |
|
|
|
# trampoline- we are final recipient of HTLC |
|
|
|
|
|
|
|
# note: the returned payment_key will contain the inner payment_secret |
|
|
|
return self.maybe_fulfill_htlc( |
|
|
|
return self.maybe_fulfill_htlc( |
|
|
|
chan=chan, |
|
|
|
chan=chan, |
|
|
|
htlc=htlc, |
|
|
|
htlc=htlc, |
|
|
|
@ -2153,7 +2154,7 @@ class Peer(Logger): |
|
|
|
outer_onion=processed_onion, |
|
|
|
outer_onion=processed_onion, |
|
|
|
trampoline_onion=trampoline_onion, |
|
|
|
trampoline_onion=trampoline_onion, |
|
|
|
fw_payment_key=payment_key) |
|
|
|
fw_payment_key=payment_key) |
|
|
|
return payment_key, None, callback |
|
|
|
return None, (payment_key, callback) |
|
|
|
|
|
|
|
|
|
|
|
# TODO don't accept payments twice for same invoice |
|
|
|
# TODO don't accept payments twice for same invoice |
|
|
|
# TODO check invoice expiry |
|
|
|
# TODO check invoice expiry |
|
|
|
@ -2180,18 +2181,18 @@ class Peer(Logger): |
|
|
|
hold_invoice_callback = self.lnworker.hold_invoice_callbacks.get(payment_hash) |
|
|
|
hold_invoice_callback = self.lnworker.hold_invoice_callbacks.get(payment_hash) |
|
|
|
if hold_invoice_callback and not preimage: |
|
|
|
if hold_invoice_callback and not preimage: |
|
|
|
callback = lambda: hold_invoice_callback(payment_hash) |
|
|
|
callback = lambda: hold_invoice_callback(payment_hash) |
|
|
|
return payment_key, None, callback |
|
|
|
return None, (payment_key, callback) |
|
|
|
|
|
|
|
|
|
|
|
if not preimage: |
|
|
|
if not preimage: |
|
|
|
if not already_forwarded: |
|
|
|
if not already_forwarded: |
|
|
|
log_fail_reason(f"missing preimage and no hold invoice callback {payment_hash.hex()}") |
|
|
|
log_fail_reason(f"missing preimage and no hold invoice callback {payment_hash.hex()}") |
|
|
|
raise exc_incorrect_or_unknown_pd |
|
|
|
raise exc_incorrect_or_unknown_pd |
|
|
|
else: |
|
|
|
else: |
|
|
|
return payment_key, None, None |
|
|
|
return None, None |
|
|
|
|
|
|
|
|
|
|
|
chan.opening_fee = None |
|
|
|
chan.opening_fee = None |
|
|
|
self.logger.info(f"maybe_fulfill_htlc. will FULFILL HTLC: chan {chan.short_channel_id}. htlc={str(htlc)}") |
|
|
|
self.logger.info(f"maybe_fulfill_htlc. will FULFILL HTLC: chan {chan.short_channel_id}. htlc={str(htlc)}") |
|
|
|
return payment_key, preimage, None |
|
|
|
return preimage, None |
|
|
|
|
|
|
|
|
|
|
|
def fulfill_htlc(self, chan: Channel, htlc_id: int, preimage: bytes): |
|
|
|
def fulfill_htlc(self, chan: Channel, htlc_id: int, preimage: bytes): |
|
|
|
self.logger.info(f"_fulfill_htlc. chan {chan.short_channel_id}. htlc_id {htlc_id}") |
|
|
|
self.logger.info(f"_fulfill_htlc. chan {chan.short_channel_id}. htlc_id {htlc_id}") |
|
|
|
@ -2700,11 +2701,7 @@ class Peer(Logger): |
|
|
|
payment_hash=payment_hash, |
|
|
|
payment_hash=payment_hash, |
|
|
|
onion_packet_bytes=onion_packet_bytes) |
|
|
|
onion_packet_bytes=onion_packet_bytes) |
|
|
|
|
|
|
|
|
|
|
|
htlc_key = serialize_htlc_key(chan.get_scid_or_local_alias(), htlc.htlc_id) |
|
|
|
preimage, forwarding_info = self.maybe_fulfill_htlc( |
|
|
|
error_bytes = error_reason = None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# fixme: do we need the outer key? |
|
|
|
|
|
|
|
payment_key, preimage, forwarding_callback = self.maybe_fulfill_htlc( |
|
|
|
|
|
|
|
chan=chan, |
|
|
|
chan=chan, |
|
|
|
htlc=htlc, |
|
|
|
htlc=htlc, |
|
|
|
processed_onion=processed_onion, |
|
|
|
processed_onion=processed_onion, |
|
|
|
@ -2712,17 +2709,18 @@ class Peer(Logger): |
|
|
|
already_forwarded=bool(forwarding_key)) |
|
|
|
already_forwarded=bool(forwarding_key)) |
|
|
|
|
|
|
|
|
|
|
|
if not forwarding_key: |
|
|
|
if not forwarding_key: |
|
|
|
if forwarding_callback: |
|
|
|
if forwarding_info: |
|
|
|
# HTLC we are supposed to forward, but haven't forwarded yet |
|
|
|
# HTLC we are supposed to forward, but haven't forwarded yet |
|
|
|
|
|
|
|
payment_key, forwarding_callback = forwarding_info |
|
|
|
if not self.lnworker.enable_htlc_forwarding: |
|
|
|
if not self.lnworker.enable_htlc_forwarding: |
|
|
|
return None, None, None |
|
|
|
return None, None, None |
|
|
|
assert payment_key |
|
|
|
|
|
|
|
if payment_key not in self.lnworker.active_forwardings: |
|
|
|
if payment_key not in self.lnworker.active_forwardings: |
|
|
|
async def wrapped_callback(): |
|
|
|
async def wrapped_callback(): |
|
|
|
forwarding_coro = forwarding_callback() |
|
|
|
forwarding_coro = forwarding_callback() |
|
|
|
try: |
|
|
|
try: |
|
|
|
next_htlc = await forwarding_coro |
|
|
|
next_htlc = await forwarding_coro |
|
|
|
if next_htlc: |
|
|
|
if next_htlc: |
|
|
|
|
|
|
|
htlc_key = serialize_htlc_key(chan.get_scid_or_local_alias(), htlc.htlc_id) |
|
|
|
self.lnworker.active_forwardings[payment_key].append(next_htlc) |
|
|
|
self.lnworker.active_forwardings[payment_key].append(next_htlc) |
|
|
|
self.lnworker.downstream_to_upstream_htlc[next_htlc] = htlc_key |
|
|
|
self.lnworker.downstream_to_upstream_htlc[next_htlc] = htlc_key |
|
|
|
except OnionRoutingFailure as e: |
|
|
|
except OnionRoutingFailure as e: |
|
|
|
@ -2734,22 +2732,26 @@ class Peer(Logger): |
|
|
|
fut = asyncio.ensure_future(wrapped_callback()) |
|
|
|
fut = asyncio.ensure_future(wrapped_callback()) |
|
|
|
# return payment_key so this branch will not be executed again |
|
|
|
# return payment_key so this branch will not be executed again |
|
|
|
return None, payment_key, None |
|
|
|
return None, payment_key, None |
|
|
|
|
|
|
|
elif preimage: |
|
|
|
|
|
|
|
return preimage, None, None |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
# we are waiting for mpp consolidation or preimage |
|
|
|
|
|
|
|
return None, None, None |
|
|
|
else: |
|
|
|
else: |
|
|
|
assert payment_key == forwarding_key |
|
|
|
|
|
|
|
# HTLC we are supposed to forward, and have already forwarded |
|
|
|
# HTLC we are supposed to forward, and have already forwarded |
|
|
|
|
|
|
|
# for trampoline onions, forwarding failures are stored with forwarding_key (which is the inner key) |
|
|
|
|
|
|
|
payment_key = forwarding_key |
|
|
|
preimage = self.lnworker.get_preimage(payment_hash) |
|
|
|
preimage = self.lnworker.get_preimage(payment_hash) |
|
|
|
error_bytes, error_reason = self.lnworker.get_forwarding_failure(payment_key) |
|
|
|
error_bytes, error_reason = self.lnworker.get_forwarding_failure(payment_key) |
|
|
|
|
|
|
|
if error_bytes or error_reason or preimage: |
|
|
|
if error_bytes or error_reason or preimage: |
|
|
|
self.lnworker.maybe_cleanup_forwarding(payment_key, chan.get_scid_or_local_alias(), htlc) |
|
|
|
self.lnworker.maybe_cleanup_forwarding(payment_key, chan.get_scid_or_local_alias(), htlc) |
|
|
|
if error_bytes: |
|
|
|
|
|
|
|
return None, None, error_bytes |
|
|
|
if error_bytes: |
|
|
|
if error_reason: |
|
|
|
return None, None, error_bytes |
|
|
|
raise error_reason |
|
|
|
if error_reason: |
|
|
|
if preimage: |
|
|
|
raise error_reason |
|
|
|
return preimage, None, None |
|
|
|
if preimage: |
|
|
|
return None, None, None |
|
|
|
return preimage, None, None |
|
|
|
|
|
|
|
return None, None, None |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def process_onion_packet( |
|
|
|
def process_onion_packet( |
|
|
|
self, |
|
|
|
self, |
|
|
|
|