make maybe_fulfill_htlc, maybe_forward_htlc synchronous.
move async operations to lnworker.htlc_switch
This commit is contained in:
@@ -1146,19 +1146,15 @@ class Peer(Logger):
|
|||||||
htlc_id=htlc_id)
|
htlc_id=htlc_id)
|
||||||
chan.receive_htlc(htlc, onion_packet)
|
chan.receive_htlc(htlc, onion_packet)
|
||||||
|
|
||||||
|
def maybe_forward_htlc(self, chan: Channel, htlc: UpdateAddHtlc, *,
|
||||||
@log_exceptions
|
onion_packet: OnionPacket, processed_onion: ProcessedOnionPacket):
|
||||||
async def _maybe_forward_htlc(self, chan: Channel, htlc: UpdateAddHtlc, *,
|
|
||||||
onion_packet: OnionPacket, processed_onion: ProcessedOnionPacket):
|
|
||||||
# Forward HTLC
|
# Forward HTLC
|
||||||
# FIXME: this is not robust to us going offline before payment is fulfilled
|
|
||||||
# FIXME: there are critical safety checks MISSING here
|
# FIXME: there are critical safety checks MISSING here
|
||||||
forwarding_enabled = self.network.config.get('lightning_forward_payments', False)
|
forwarding_enabled = self.network.config.get('lightning_forward_payments', False)
|
||||||
if not forwarding_enabled:
|
if not forwarding_enabled:
|
||||||
self.logger.info(f"forwarding is disabled. failing htlc.")
|
self.logger.info(f"forwarding is disabled. failing htlc.")
|
||||||
reason = OnionRoutingFailureMessage(code=OnionFailureCode.PERMANENT_CHANNEL_FAILURE, data=b'')
|
reason = OnionRoutingFailureMessage(code=OnionFailureCode.PERMANENT_CHANNEL_FAILURE, data=b'')
|
||||||
await self.fail_htlc(chan, htlc.htlc_id, onion_packet, reason)
|
return False, False, reason
|
||||||
return
|
|
||||||
dph = processed_onion.hop_data.per_hop
|
dph = processed_onion.hop_data.per_hop
|
||||||
next_chan = self.lnworker.get_channel_by_short_id(dph.short_channel_id)
|
next_chan = self.lnworker.get_channel_by_short_id(dph.short_channel_id)
|
||||||
next_chan_scid = dph.short_channel_id
|
next_chan_scid = dph.short_channel_id
|
||||||
@@ -1167,8 +1163,7 @@ class Peer(Logger):
|
|||||||
if next_chan is None:
|
if next_chan is None:
|
||||||
self.logger.info(f"cannot forward htlc. cannot find next_chan {next_chan_scid}")
|
self.logger.info(f"cannot forward htlc. cannot find next_chan {next_chan_scid}")
|
||||||
reason = OnionRoutingFailureMessage(code=OnionFailureCode.UNKNOWN_NEXT_PEER, data=b'')
|
reason = OnionRoutingFailureMessage(code=OnionFailureCode.UNKNOWN_NEXT_PEER, data=b'')
|
||||||
await self.fail_htlc(chan, htlc.htlc_id, onion_packet, reason)
|
return False, False, reason
|
||||||
return
|
|
||||||
outgoing_chan_upd = next_chan.get_outgoing_gossip_channel_update()[2:]
|
outgoing_chan_upd = next_chan.get_outgoing_gossip_channel_update()[2:]
|
||||||
outgoing_chan_upd_len = len(outgoing_chan_upd).to_bytes(2, byteorder="big")
|
outgoing_chan_upd_len = len(outgoing_chan_upd).to_bytes(2, byteorder="big")
|
||||||
if not next_chan.can_send_update_add_htlc():
|
if not next_chan.can_send_update_add_htlc():
|
||||||
@@ -1176,25 +1171,21 @@ class Peer(Logger):
|
|||||||
f"chan state {next_chan.get_state()}, peer state: {next_chan.peer_state}")
|
f"chan state {next_chan.get_state()}, peer state: {next_chan.peer_state}")
|
||||||
reason = OnionRoutingFailureMessage(code=OnionFailureCode.TEMPORARY_CHANNEL_FAILURE,
|
reason = OnionRoutingFailureMessage(code=OnionFailureCode.TEMPORARY_CHANNEL_FAILURE,
|
||||||
data=outgoing_chan_upd_len+outgoing_chan_upd)
|
data=outgoing_chan_upd_len+outgoing_chan_upd)
|
||||||
await self.fail_htlc(chan, htlc.htlc_id, onion_packet, reason)
|
return False, False, reason
|
||||||
return
|
|
||||||
next_cltv_expiry = int.from_bytes(dph.outgoing_cltv_value, 'big')
|
next_cltv_expiry = int.from_bytes(dph.outgoing_cltv_value, 'big')
|
||||||
if htlc.cltv_expiry - next_cltv_expiry < NBLOCK_OUR_CLTV_EXPIRY_DELTA:
|
if htlc.cltv_expiry - next_cltv_expiry < NBLOCK_OUR_CLTV_EXPIRY_DELTA:
|
||||||
reason = OnionRoutingFailureMessage(code=OnionFailureCode.INCORRECT_CLTV_EXPIRY,
|
reason = OnionRoutingFailureMessage(code=OnionFailureCode.INCORRECT_CLTV_EXPIRY,
|
||||||
data=(htlc.cltv_expiry.to_bytes(4, byteorder="big")
|
data=(htlc.cltv_expiry.to_bytes(4, byteorder="big")
|
||||||
+ outgoing_chan_upd_len + outgoing_chan_upd))
|
+ outgoing_chan_upd_len + outgoing_chan_upd))
|
||||||
await self.fail_htlc(chan, htlc.htlc_id, onion_packet, reason)
|
return False, False, reason
|
||||||
return
|
|
||||||
if htlc.cltv_expiry - lnutil.NBLOCK_DEADLINE_BEFORE_EXPIRY_FOR_RECEIVED_HTLCS <= local_height \
|
if htlc.cltv_expiry - lnutil.NBLOCK_DEADLINE_BEFORE_EXPIRY_FOR_RECEIVED_HTLCS <= local_height \
|
||||||
or next_cltv_expiry <= local_height:
|
or next_cltv_expiry <= local_height:
|
||||||
reason = OnionRoutingFailureMessage(code=OnionFailureCode.EXPIRY_TOO_SOON,
|
reason = OnionRoutingFailureMessage(code=OnionFailureCode.EXPIRY_TOO_SOON,
|
||||||
data=outgoing_chan_upd_len+outgoing_chan_upd)
|
data=outgoing_chan_upd_len+outgoing_chan_upd)
|
||||||
await self.fail_htlc(chan, htlc.htlc_id, onion_packet, reason)
|
return False, False,reason
|
||||||
return
|
|
||||||
if max(htlc.cltv_expiry, next_cltv_expiry) > local_height + lnutil.NBLOCK_CLTV_EXPIRY_TOO_FAR_INTO_FUTURE:
|
if max(htlc.cltv_expiry, next_cltv_expiry) > local_height + lnutil.NBLOCK_CLTV_EXPIRY_TOO_FAR_INTO_FUTURE:
|
||||||
reason = OnionRoutingFailureMessage(code=OnionFailureCode.EXPIRY_TOO_FAR, data=b'')
|
reason = OnionRoutingFailureMessage(code=OnionFailureCode.EXPIRY_TOO_FAR, data=b'')
|
||||||
await self.fail_htlc(chan, htlc.htlc_id, onion_packet, reason)
|
return False, False, reason
|
||||||
return
|
|
||||||
next_amount_msat_htlc = int.from_bytes(dph.amt_to_forward, 'big')
|
next_amount_msat_htlc = int.from_bytes(dph.amt_to_forward, 'big')
|
||||||
forwarding_fees = fee_for_edge_msat(forwarded_amount_msat=next_amount_msat_htlc,
|
forwarding_fees = fee_for_edge_msat(forwarded_amount_msat=next_amount_msat_htlc,
|
||||||
fee_base_msat=lnutil.OUR_FEE_BASE_MSAT,
|
fee_base_msat=lnutil.OUR_FEE_BASE_MSAT,
|
||||||
@@ -1203,13 +1194,10 @@ class Peer(Logger):
|
|||||||
reason = OnionRoutingFailureMessage(code=OnionFailureCode.FEE_INSUFFICIENT,
|
reason = OnionRoutingFailureMessage(code=OnionFailureCode.FEE_INSUFFICIENT,
|
||||||
data=(next_amount_msat_htlc.to_bytes(8, byteorder="big")
|
data=(next_amount_msat_htlc.to_bytes(8, byteorder="big")
|
||||||
+ outgoing_chan_upd_len + outgoing_chan_upd))
|
+ outgoing_chan_upd_len + outgoing_chan_upd))
|
||||||
await self.fail_htlc(chan, htlc.htlc_id, onion_packet, reason)
|
return False, False, reason
|
||||||
return
|
|
||||||
|
|
||||||
self.logger.info(f'forwarding htlc to {next_chan.node_id}')
|
self.logger.info(f'forwarding htlc to {next_chan.node_id}')
|
||||||
next_htlc = UpdateAddHtlc(amount_msat=next_amount_msat_htlc, payment_hash=htlc.payment_hash, cltv_expiry=next_cltv_expiry, timestamp=int(time.time()))
|
next_htlc = UpdateAddHtlc(amount_msat=next_amount_msat_htlc, payment_hash=htlc.payment_hash, cltv_expiry=next_cltv_expiry, timestamp=int(time.time()))
|
||||||
next_htlc = next_chan.add_htlc(next_htlc)
|
next_htlc = next_chan.add_htlc(next_htlc)
|
||||||
next_remote_ctn = next_chan.get_latest_ctn(REMOTE)
|
|
||||||
next_peer.send_message(
|
next_peer.send_message(
|
||||||
"update_add_htlc",
|
"update_add_htlc",
|
||||||
channel_id=next_chan.channel_id,
|
channel_id=next_chan.channel_id,
|
||||||
@@ -1219,52 +1207,37 @@ class Peer(Logger):
|
|||||||
payment_hash=next_htlc.payment_hash,
|
payment_hash=next_htlc.payment_hash,
|
||||||
onion_routing_packet=processed_onion.next_packet.to_bytes()
|
onion_routing_packet=processed_onion.next_packet.to_bytes()
|
||||||
)
|
)
|
||||||
await next_peer.await_remote(next_chan, next_remote_ctn)
|
return next_chan, next_htlc, None
|
||||||
success, preimage, reason = await self.lnworker.await_payment(next_htlc.payment_hash)
|
|
||||||
if success:
|
|
||||||
await self._fulfill_htlc(chan, htlc.htlc_id, preimage)
|
|
||||||
self.logger.info("htlc forwarded successfully")
|
|
||||||
else:
|
|
||||||
# TODO: test this
|
|
||||||
self.logger.info(f"forwarded htlc has failed, {reason}")
|
|
||||||
await self.fail_htlc(chan, htlc.htlc_id, onion_packet, reason)
|
|
||||||
|
|
||||||
@log_exceptions
|
def maybe_fulfill_htlc(self, chan: Channel, htlc: UpdateAddHtlc, *,
|
||||||
async def _maybe_fulfill_htlc(self, chan: Channel, htlc: UpdateAddHtlc, *,
|
onion_packet: OnionPacket, processed_onion: ProcessedOnionPacket):
|
||||||
onion_packet: OnionPacket, processed_onion: ProcessedOnionPacket):
|
|
||||||
try:
|
try:
|
||||||
info = self.lnworker.get_payment_info(htlc.payment_hash)
|
info = self.lnworker.get_payment_info(htlc.payment_hash)
|
||||||
preimage = self.lnworker.get_preimage(htlc.payment_hash)
|
preimage = self.lnworker.get_preimage(htlc.payment_hash)
|
||||||
except UnknownPaymentHash:
|
except UnknownPaymentHash:
|
||||||
reason = OnionRoutingFailureMessage(code=OnionFailureCode.INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS, data=b'')
|
reason = OnionRoutingFailureMessage(code=OnionFailureCode.INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS, data=b'')
|
||||||
await self.fail_htlc(chan, htlc.htlc_id, onion_packet, reason)
|
return False, reason
|
||||||
return
|
|
||||||
expected_received_msat = int(info.amount * 1000) if info.amount is not None else None
|
expected_received_msat = int(info.amount * 1000) if info.amount is not None else None
|
||||||
if expected_received_msat is not None and \
|
if expected_received_msat is not None and \
|
||||||
not (expected_received_msat <= htlc.amount_msat <= 2 * expected_received_msat):
|
not (expected_received_msat <= htlc.amount_msat <= 2 * expected_received_msat):
|
||||||
reason = OnionRoutingFailureMessage(code=OnionFailureCode.INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS, data=b'')
|
reason = OnionRoutingFailureMessage(code=OnionFailureCode.INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS, data=b'')
|
||||||
await self.fail_htlc(chan, htlc.htlc_id, onion_packet, reason)
|
return False, reason
|
||||||
return
|
|
||||||
local_height = self.network.get_local_height()
|
local_height = self.network.get_local_height()
|
||||||
if local_height + MIN_FINAL_CLTV_EXPIRY_ACCEPTED > htlc.cltv_expiry:
|
if local_height + MIN_FINAL_CLTV_EXPIRY_ACCEPTED > htlc.cltv_expiry:
|
||||||
reason = OnionRoutingFailureMessage(code=OnionFailureCode.FINAL_EXPIRY_TOO_SOON, data=b'')
|
reason = OnionRoutingFailureMessage(code=OnionFailureCode.FINAL_EXPIRY_TOO_SOON, data=b'')
|
||||||
await self.fail_htlc(chan, htlc.htlc_id, onion_packet, reason)
|
return False, reason
|
||||||
return
|
|
||||||
cltv_from_onion = int.from_bytes(processed_onion.hop_data.per_hop.outgoing_cltv_value, byteorder="big")
|
cltv_from_onion = int.from_bytes(processed_onion.hop_data.per_hop.outgoing_cltv_value, byteorder="big")
|
||||||
if cltv_from_onion != htlc.cltv_expiry:
|
if cltv_from_onion != htlc.cltv_expiry:
|
||||||
reason = OnionRoutingFailureMessage(code=OnionFailureCode.FINAL_INCORRECT_CLTV_EXPIRY,
|
reason = OnionRoutingFailureMessage(code=OnionFailureCode.FINAL_INCORRECT_CLTV_EXPIRY,
|
||||||
data=htlc.cltv_expiry.to_bytes(4, byteorder="big"))
|
data=htlc.cltv_expiry.to_bytes(4, byteorder="big"))
|
||||||
await self.fail_htlc(chan, htlc.htlc_id, onion_packet, reason)
|
return False, reason
|
||||||
return
|
|
||||||
amount_from_onion = int.from_bytes(processed_onion.hop_data.per_hop.amt_to_forward, byteorder="big")
|
amount_from_onion = int.from_bytes(processed_onion.hop_data.per_hop.amt_to_forward, byteorder="big")
|
||||||
if amount_from_onion > htlc.amount_msat:
|
if amount_from_onion > htlc.amount_msat:
|
||||||
reason = OnionRoutingFailureMessage(code=OnionFailureCode.FINAL_INCORRECT_HTLC_AMOUNT,
|
reason = OnionRoutingFailureMessage(code=OnionFailureCode.FINAL_INCORRECT_HTLC_AMOUNT,
|
||||||
data=htlc.amount_msat.to_bytes(8, byteorder="big"))
|
data=htlc.amount_msat.to_bytes(8, byteorder="big"))
|
||||||
await self.fail_htlc(chan, htlc.htlc_id, onion_packet, reason)
|
return False, reason
|
||||||
return
|
# all good
|
||||||
#self.network.trigger_callback('htlc_added', htlc, invoice, RECEIVED)
|
return preimage, None
|
||||||
await self.lnworker.enable_htlc_settle.wait()
|
|
||||||
await self._fulfill_htlc(chan, htlc.htlc_id, preimage)
|
|
||||||
|
|
||||||
async def _fulfill_htlc(self, chan: Channel, htlc_id: int, preimage: bytes):
|
async 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}")
|
||||||
|
|||||||
@@ -1348,19 +1348,38 @@ class LNWallet(LNWorker):
|
|||||||
payment_hash = htlc.payment_hash
|
payment_hash = htlc.payment_hash
|
||||||
processed_onion = process_onion_packet(onion_packet, associated_data=payment_hash, our_onion_private_key=peer.privkey)
|
processed_onion = process_onion_packet(onion_packet, associated_data=payment_hash, our_onion_private_key=peer.privkey)
|
||||||
if processed_onion.are_we_final:
|
if processed_onion.are_we_final:
|
||||||
await peer._maybe_fulfill_htlc(
|
preimage, reason = peer.maybe_fulfill_htlc(
|
||||||
chan=chan,
|
chan=chan,
|
||||||
htlc=htlc,
|
htlc=htlc,
|
||||||
onion_packet=onion_packet,
|
onion_packet=onion_packet,
|
||||||
processed_onion=processed_onion)
|
processed_onion=processed_onion)
|
||||||
|
if preimage:
|
||||||
|
await self.enable_htlc_settle.wait()
|
||||||
|
await peer._fulfill_htlc(chan, htlc.htlc_id, preimage)
|
||||||
|
else:
|
||||||
|
await peer.fail_htlc(chan, htlc.htlc_id, onion_packet, reason)
|
||||||
else:
|
else:
|
||||||
# todo: if we are forwarding we need to test next peer's state
|
# todo: if we are forwarding we need to test next peer's state
|
||||||
# we should dissociate forwarding and fulfillment
|
# we should dissociate forwarding and fulfillment
|
||||||
await peer._maybe_forward_htlc(
|
next_chan, next_htlc, reason = peer.maybe_forward_htlc(
|
||||||
chan=chan,
|
chan=chan,
|
||||||
htlc=htlc,
|
htlc=htlc,
|
||||||
onion_packet=onion_packet,
|
onion_packet=onion_packet,
|
||||||
processed_onion=processed_onion)
|
processed_onion=processed_onion)
|
||||||
|
if not next_chan:
|
||||||
|
await self.fail_htlc(chan, htlc.htlc_id, onion_packet, reason)
|
||||||
|
else:
|
||||||
|
next_peer = self.peers[next_chan.node_id]
|
||||||
|
next_remote_ctn = next_chan.get_latest_ctn(REMOTE)
|
||||||
|
await next_peer.await_remote(next_chan, next_remote_ctn)
|
||||||
|
success, preimage, reason = await self.await_payment(next_htlc.payment_hash)
|
||||||
|
if success:
|
||||||
|
await peer._fulfill_htlc(chan, htlc.htlc_id, preimage)
|
||||||
|
self.logger.info("htlc forwarded successfully")
|
||||||
|
else:
|
||||||
|
# TODO: test this
|
||||||
|
self.logger.info(f"forwarded htlc has failed, {reason}")
|
||||||
|
await peer.fail_htlc(chan, htlc.htlc_id, onion_packet, reason)
|
||||||
done.add(htlc_id)
|
done.add(htlc_id)
|
||||||
# cleanup
|
# cleanup
|
||||||
for htlc_id in done:
|
for htlc_id in done:
|
||||||
|
|||||||
Reference in New Issue
Block a user