Browse Source

Merge pull request #8924 from spesmilo/fix_swapserver_trampoline

Fix swapserver trampoline
master
ThomasV 2 years ago committed by GitHub
parent
commit
33b1946202
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 60
      electrum/lnpeer.py
  2. 1
      tests/regtest.py

60
electrum/lnpeer.py

@ -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,

1
tests/regtest.py

@ -78,6 +78,7 @@ class TestLightningAB(TestLightning):
class TestLightningSwapserver(TestLightning): class TestLightningSwapserver(TestLightning):
agents = { agents = {
'alice': { 'alice': {
'use_gossip': 'false',
}, },
'bob': { 'bob': {
'lightning_listen': 'localhost:9735', 'lightning_listen': 'localhost:9735',

Loading…
Cancel
Save