lnpeer: obfuscate error pakets of forwarded htlcs, that we
propageate back to the sender. lnworker: in htlc_fulfilled and htlc_failed, return early if the htlc was forwarded, so that we do not trigger invoice callbacks
This commit is contained in:
@@ -396,7 +396,7 @@ class OnionRoutingFailure(Exception):
|
|||||||
|
|
||||||
def construct_onion_error(
|
def construct_onion_error(
|
||||||
reason: OnionRoutingFailure,
|
reason: OnionRoutingFailure,
|
||||||
onion_packet: OnionPacket,
|
their_public_key: bytes,
|
||||||
our_onion_private_key: bytes,
|
our_onion_private_key: bytes,
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
# create payload
|
# create payload
|
||||||
@@ -409,11 +409,14 @@ def construct_onion_error(
|
|||||||
error_packet += pad_len.to_bytes(2, byteorder="big")
|
error_packet += pad_len.to_bytes(2, byteorder="big")
|
||||||
error_packet += bytes(pad_len)
|
error_packet += bytes(pad_len)
|
||||||
# add hmac
|
# add hmac
|
||||||
shared_secret = get_ecdh(our_onion_private_key, onion_packet.public_key)
|
shared_secret = get_ecdh(our_onion_private_key, their_public_key)
|
||||||
um_key = get_bolt04_onion_key(b'um', shared_secret)
|
um_key = get_bolt04_onion_key(b'um', shared_secret)
|
||||||
hmac_ = hmac_oneshot(um_key, msg=error_packet, digest=hashlib.sha256)
|
hmac_ = hmac_oneshot(um_key, msg=error_packet, digest=hashlib.sha256)
|
||||||
error_packet = hmac_ + error_packet
|
error_packet = hmac_ + error_packet
|
||||||
# obfuscate
|
return error_packet
|
||||||
|
|
||||||
|
def obfuscate_onion_error(error_packet, their_public_key, our_onion_private_key):
|
||||||
|
shared_secret = get_ecdh(our_onion_private_key, their_public_key)
|
||||||
ammag_key = get_bolt04_onion_key(b'ammag', shared_secret)
|
ammag_key = get_bolt04_onion_key(b'ammag', shared_secret)
|
||||||
stream_bytes = generate_cipher_stream(ammag_key, len(error_packet))
|
stream_bytes = generate_cipher_stream(ammag_key, len(error_packet))
|
||||||
error_packet = xor_bytes(error_packet, stream_bytes)
|
error_packet = xor_bytes(error_packet, stream_bytes)
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ from .bitcoin import make_op_return, DummyAddress
|
|||||||
from .transaction import PartialTxOutput, match_script_against_template, Sighash
|
from .transaction import PartialTxOutput, match_script_against_template, Sighash
|
||||||
from .logging import Logger
|
from .logging import Logger
|
||||||
from .lnonion import (new_onion_packet, OnionFailureCode, calc_hops_data_for_payment,
|
from .lnonion import (new_onion_packet, OnionFailureCode, calc_hops_data_for_payment,
|
||||||
process_onion_packet, OnionPacket, construct_onion_error, OnionRoutingFailure,
|
process_onion_packet, OnionPacket, construct_onion_error, obfuscate_onion_error, OnionRoutingFailure,
|
||||||
ProcessedOnionPacket, UnsupportedOnionPacketVersion, InvalidOnionMac, InvalidOnionPubkey,
|
ProcessedOnionPacket, UnsupportedOnionPacketVersion, InvalidOnionMac, InvalidOnionPubkey,
|
||||||
OnionFailureCodeMetaFlag)
|
OnionFailureCodeMetaFlag)
|
||||||
from .lnchannel import Channel, RevokeAndAck, RemoteCtnTooFarInFuture, ChannelState, PeerState, ChanCloseOption, CF_ANNOUNCE_CHANNEL
|
from .lnchannel import Channel, RevokeAndAck, RemoteCtnTooFarInFuture, ChannelState, PeerState, ChanCloseOption, CF_ANNOUNCE_CHANNEL
|
||||||
@@ -2405,7 +2405,9 @@ class Peer(Logger):
|
|||||||
onion_packet_bytes=onion_packet_bytes,
|
onion_packet_bytes=onion_packet_bytes,
|
||||||
onion_packet=onion_packet)
|
onion_packet=onion_packet)
|
||||||
except OnionRoutingFailure as e:
|
except OnionRoutingFailure as e:
|
||||||
error_bytes = construct_onion_error(e, onion_packet, our_onion_private_key=self.privkey)
|
error_bytes = construct_onion_error(e, onion_packet.public_key, our_onion_private_key=self.privkey)
|
||||||
|
if error_bytes:
|
||||||
|
error_bytes = obfuscate_onion_error(error_bytes, onion_packet.public_key, our_onion_private_key=self.privkey)
|
||||||
if fw_info:
|
if fw_info:
|
||||||
unfulfilled[htlc_id] = local_ctn, remote_ctn, onion_packet_hex, fw_info
|
unfulfilled[htlc_id] = local_ctn, remote_ctn, onion_packet_hex, fw_info
|
||||||
self.lnworker.downstream_htlc_to_upstream_peer_map[fw_info] = self.pubkey
|
self.lnworker.downstream_htlc_to_upstream_peer_map[fw_info] = self.pubkey
|
||||||
|
|||||||
@@ -2281,23 +2281,24 @@ class LNWallet(LNWorker):
|
|||||||
info = info._replace(status=status)
|
info = info._replace(status=status)
|
||||||
self.save_payment_info(info)
|
self.save_payment_info(info)
|
||||||
|
|
||||||
def _on_maybe_forwarded_htlc_resolved(self, chan: Channel, htlc_id: int) -> None:
|
def is_forwarded_htlc_notify(self, chan: Channel, htlc_id: int) -> None:
|
||||||
"""Called when an HTLC we offered on chan gets irrevocably fulfilled or failed.
|
"""Called when an HTLC we offered on chan gets irrevocably fulfilled or failed.
|
||||||
If we find this was a forwarded HTLC, the upstream peer is notified.
|
If we find this was a forwarded HTLC, the upstream peer is notified.
|
||||||
"""
|
"""
|
||||||
fw_info = chan.short_channel_id.hex(), htlc_id
|
fw_info = chan.short_channel_id.hex(), htlc_id
|
||||||
upstream_peer_pubkey = self.downstream_htlc_to_upstream_peer_map.get(fw_info)
|
upstream_peer_pubkey = self.downstream_htlc_to_upstream_peer_map.get(fw_info)
|
||||||
if not upstream_peer_pubkey:
|
if not upstream_peer_pubkey:
|
||||||
return
|
return False
|
||||||
upstream_peer = self.peers.get(upstream_peer_pubkey)
|
upstream_peer = self.peers.get(upstream_peer_pubkey)
|
||||||
if not upstream_peer:
|
if upstream_peer:
|
||||||
return
|
upstream_peer.downstream_htlc_resolved_event.set()
|
||||||
upstream_peer.downstream_htlc_resolved_event.set()
|
upstream_peer.downstream_htlc_resolved_event.clear()
|
||||||
upstream_peer.downstream_htlc_resolved_event.clear()
|
return True
|
||||||
|
|
||||||
def htlc_fulfilled(self, chan: Channel, payment_hash: bytes, htlc_id: int):
|
def htlc_fulfilled(self, chan: Channel, payment_hash: bytes, htlc_id: int):
|
||||||
util.trigger_callback('htlc_fulfilled', payment_hash, chan, htlc_id)
|
util.trigger_callback('htlc_fulfilled', payment_hash, chan, htlc_id)
|
||||||
self._on_maybe_forwarded_htlc_resolved(chan=chan, htlc_id=htlc_id)
|
if self.is_forwarded_htlc_notify(chan=chan, htlc_id=htlc_id):
|
||||||
|
return
|
||||||
q = None
|
q = None
|
||||||
if shi := self.sent_htlcs_info.get((payment_hash, chan.short_channel_id, htlc_id)):
|
if shi := self.sent_htlcs_info.get((payment_hash, chan.short_channel_id, htlc_id)):
|
||||||
chan.pop_onion_key(htlc_id)
|
chan.pop_onion_key(htlc_id)
|
||||||
@@ -2328,7 +2329,8 @@ class LNWallet(LNWorker):
|
|||||||
failure_message: Optional['OnionRoutingFailure']):
|
failure_message: Optional['OnionRoutingFailure']):
|
||||||
|
|
||||||
util.trigger_callback('htlc_failed', payment_hash, chan, htlc_id)
|
util.trigger_callback('htlc_failed', payment_hash, chan, htlc_id)
|
||||||
self._on_maybe_forwarded_htlc_resolved(chan=chan, htlc_id=htlc_id)
|
if self.is_forwarded_htlc_notify(chan=chan, htlc_id=htlc_id):
|
||||||
|
return
|
||||||
q = None
|
q = None
|
||||||
if shi := self.sent_htlcs_info.get((payment_hash, chan.short_channel_id, htlc_id)):
|
if shi := self.sent_htlcs_info.get((payment_hash, chan.short_channel_id, htlc_id)):
|
||||||
onion_key = chan.pop_onion_key(htlc_id)
|
onion_key = chan.pop_onion_key(htlc_id)
|
||||||
|
|||||||
Reference in New Issue
Block a user