diff --git a/electrum/lnonion.py b/electrum/lnonion.py index d996ab521..0f3a7a000 100644 --- a/electrum/lnonion.py +++ b/electrum/lnonion.py @@ -396,7 +396,7 @@ class OnionRoutingFailure(Exception): def construct_onion_error( reason: OnionRoutingFailure, - onion_packet: OnionPacket, + their_public_key: bytes, our_onion_private_key: bytes, ) -> bytes: # create payload @@ -409,11 +409,14 @@ def construct_onion_error( error_packet += pad_len.to_bytes(2, byteorder="big") error_packet += bytes(pad_len) # 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) hmac_ = hmac_oneshot(um_key, msg=error_packet, digest=hashlib.sha256) 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) stream_bytes = generate_cipher_stream(ammag_key, len(error_packet)) error_packet = xor_bytes(error_packet, stream_bytes) diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py index 0f9ea9511..6ab700dba 100644 --- a/electrum/lnpeer.py +++ b/electrum/lnpeer.py @@ -28,7 +28,7 @@ from .bitcoin import make_op_return, DummyAddress from .transaction import PartialTxOutput, match_script_against_template, Sighash from .logging import Logger 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, OnionFailureCodeMetaFlag) 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=onion_packet) 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: unfulfilled[htlc_id] = local_ctn, remote_ctn, onion_packet_hex, fw_info self.lnworker.downstream_htlc_to_upstream_peer_map[fw_info] = self.pubkey diff --git a/electrum/lnworker.py b/electrum/lnworker.py index 2e9c9ba29..ef2f32c77 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -2281,23 +2281,24 @@ class LNWallet(LNWorker): info = info._replace(status=status) 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. If we find this was a forwarded HTLC, the upstream peer is notified. """ fw_info = chan.short_channel_id.hex(), htlc_id upstream_peer_pubkey = self.downstream_htlc_to_upstream_peer_map.get(fw_info) if not upstream_peer_pubkey: - return + return False upstream_peer = self.peers.get(upstream_peer_pubkey) - if not upstream_peer: - return - upstream_peer.downstream_htlc_resolved_event.set() - upstream_peer.downstream_htlc_resolved_event.clear() + if upstream_peer: + upstream_peer.downstream_htlc_resolved_event.set() + upstream_peer.downstream_htlc_resolved_event.clear() + return True def htlc_fulfilled(self, chan: Channel, payment_hash: bytes, htlc_id: int): 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 if shi := self.sent_htlcs_info.get((payment_hash, chan.short_channel_id, htlc_id)): chan.pop_onion_key(htlc_id) @@ -2328,7 +2329,8 @@ class LNWallet(LNWorker): failure_message: Optional['OnionRoutingFailure']): 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 if shi := self.sent_htlcs_info.get((payment_hash, chan.short_channel_id, htlc_id)): onion_key = chan.pop_onion_key(htlc_id)