Skip to content

Commit 6c10962

Browse files
committed
lnpeer: update_add_htlc: take blinding into account for HTLCs arriving via blinded path
1 parent 9da8ec9 commit 6c10962

File tree

3 files changed

+49
-19
lines changed

3 files changed

+49
-19
lines changed

electrum/lnpeer.py

Lines changed: 42 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@
2121
import aiorpcx
2222
from aiorpcx import ignore_after
2323

24-
from .crypto import sha256, sha256d, privkey_to_pubkey
24+
from .crypto import sha256, sha256d, privkey_to_pubkey, get_ecdh
2525
from . import bitcoin, util
2626
from . import constants
27+
from .onion_message import blinding_privkey
2728
from .util import (log_exceptions, ignore_exceptions, chunks, OldTaskGroup,
2829
UnrelatedTransactionException, error_text_bytes_to_safe_str, AsyncHangDetector,
2930
NoDynamicFeeEstimates, event_listener, EventListener)
@@ -35,7 +36,7 @@
3536
from .lnonion import (new_onion_packet, OnionFailureCode, calc_hops_data_for_payment, process_onion_packet,
3637
OnionPacket, construct_onion_error, obfuscate_onion_error, OnionRoutingFailure,
3738
ProcessedOnionPacket, UnsupportedOnionPacketVersion, InvalidOnionMac, InvalidOnionPubkey,
38-
OnionFailureCodeMetaFlag, calc_hops_data_for_blinded_payment)
39+
OnionFailureCodeMetaFlag, calc_hops_data_for_blinded_payment, decrypt_onionmsg_data_tlv)
3940
from .lnchannel import Channel, RevokeAndAck, RemoteCtnTooFarInFuture, ChannelState, PeerState, ChanCloseOption, CF_ANNOUNCE_CHANNEL
4041
from . import lnutil
4142
from .lnutil import (Outpoint, LocalConfig, RECEIVED, UpdateAddHtlc, ChannelConfig,
@@ -2063,12 +2064,14 @@ def on_update_add_htlc(self, chan: Channel, payload):
20632064
cltv_abs = payload["cltv_expiry"]
20642065
amount_msat_htlc = payload["amount_msat"]
20652066
onion_packet = payload["onion_routing_packet"]
2067+
blinding = payload.get("update_add_htlc_tlvs", {}).get("blinded_path", {}).get("path_key")
20662068
htlc = UpdateAddHtlc(
20672069
amount_msat=amount_msat_htlc,
20682070
payment_hash=payment_hash,
20692071
cltv_abs=cltv_abs,
20702072
timestamp=int(time.time()),
2071-
htlc_id=htlc_id)
2073+
htlc_id=htlc_id,
2074+
blinding=blinding)
20722075
self.logger.info(f"on_update_add_htlc. chan {chan.short_channel_id}. htlc={str(htlc)}")
20732076
if chan.get_state() != ChannelState.OPEN:
20742077
raise RemoteMisbehaving(f"received update_add_htlc while chan.get_state() != OPEN. state was {chan.get_state()!r}")
@@ -2115,11 +2118,29 @@ def check_accepted_htlc(
21152118
raise OnionRoutingFailure(
21162119
code=OnionFailureCode.FINAL_INCORRECT_CLTV_EXPIRY,
21172120
data=htlc.cltv_abs.to_bytes(4, byteorder="big"))
2118-
try:
2119-
total_msat = processed_onion.hop_data.payload["payment_data"]["total_msat"] # type: int
2120-
except Exception:
2121-
log_fail_reason(f"'total_msat' missing from onion")
2122-
raise exc_incorrect_or_unknown_pd
2121+
2122+
if htlc.blinding: # payment over blinded path
2123+
# spec: MUST return an error if the payload contains other tlv fields than encrypted_recipient_data,
2124+
# current_path_key, amt_to_forward, outgoing_cltv_value and total_amount_msat.
2125+
assert all(x in ['encrypted_recipient_data', 'current_blinding_point', 'amt_to_forward', 'outgoing_cltv_value', 'total_amount_msat']
2126+
for x in processed_onion.hop_data.payload.keys())
2127+
shared_secret = get_ecdh(self.privkey, htlc.blinding)
2128+
recipient_data = decrypt_onionmsg_data_tlv(
2129+
shared_secret=shared_secret,
2130+
encrypted_recipient_data=processed_onion.hop_data.payload['encrypted_recipient_data']['encrypted_data']
2131+
)
2132+
payment_secret_from_onion = recipient_data['path_id']['data']
2133+
try:
2134+
total_msat = processed_onion.hop_data.payload['total_amount_msat']['total_msat']
2135+
except Exception as e:
2136+
log_fail_reason(f"'total_msat' missing from onion")
2137+
raise exc_incorrect_or_unknown_pd
2138+
else:
2139+
try:
2140+
total_msat = processed_onion.hop_data.payload["payment_data"]["total_msat"] # type: int
2141+
except Exception:
2142+
log_fail_reason(f"'total_msat' missing from onion")
2143+
raise exc_incorrect_or_unknown_pd
21232144

21242145
if chan.opening_fee:
21252146
channel_opening_fee = chan.opening_fee['channel_opening_fee'] # type: int
@@ -2134,11 +2155,12 @@ def check_accepted_htlc(
21342155
code=OnionFailureCode.FINAL_INCORRECT_HTLC_AMOUNT,
21352156
data=htlc.amount_msat.to_bytes(8, byteorder="big"))
21362157

2137-
try:
2138-
payment_secret_from_onion = processed_onion.hop_data.payload["payment_data"]["payment_secret"] # type: bytes
2139-
except Exception:
2140-
log_fail_reason(f"'payment_secret' missing from onion")
2141-
raise exc_incorrect_or_unknown_pd
2158+
if not htlc.blinding:
2159+
try:
2160+
payment_secret_from_onion = processed_onion.hop_data.payload["payment_data"]["payment_secret"] # type: bytes
2161+
except Exception:
2162+
log_fail_reason(f"'payment_secret' missing from onion")
2163+
raise exc_incorrect_or_unknown_pd
21422164

21432165
return payment_secret_from_onion, total_msat, channel_opening_fee, exc_incorrect_or_unknown_pd
21442166

@@ -2794,7 +2816,7 @@ async def htlc_switch(self):
27942816
onion_packet = None
27952817
try:
27962818
onion_packet = OnionPacket.from_bytes(onion_packet_bytes)
2797-
except OnionRoutingFailure as e:
2819+
except OnionRoutingFailure as e: # NOTE: never reached?
27982820
error_reason = e
27992821
else:
28002822
try:
@@ -2808,6 +2830,7 @@ async def htlc_switch(self):
28082830
assert forwarding_key is None
28092831
unfulfilled[htlc_id] = onion_packet_hex, _forwarding_key
28102832
except OnionRoutingFailure as e:
2833+
self.logger.debug(f'OnionRoutingFailure: {e.code!r}')
28112834
error_bytes = construct_onion_error(e, onion_packet.public_key, self.privkey, self.network.get_local_height())
28122835
if error_bytes:
28132836
error_bytes = obfuscate_onion_error(error_bytes, onion_packet.public_key, our_onion_private_key=self.privkey)
@@ -2874,6 +2897,7 @@ def process_unfulfilled_htlc(
28742897
processed_onion = self.process_onion_packet(
28752898
onion_packet,
28762899
payment_hash=payment_hash,
2900+
blinding=htlc.blinding,
28772901
onion_packet_bytes=onion_packet_bytes)
28782902

28792903
preimage, forwarding_info = self.maybe_fulfill_htlc(
@@ -2940,13 +2964,16 @@ def process_onion_packet(
29402964
onion_packet: OnionPacket, *,
29412965
payment_hash: bytes,
29422966
onion_packet_bytes: bytes,
2967+
blinding: bytes = None,
29432968
is_trampoline: bool = False) -> ProcessedOnionPacket:
29442969

29452970
failure_data = sha256(onion_packet_bytes)
2971+
privkey = blinding_privkey(privkey=self.privkey, blinding=blinding) if blinding else self.privkey
2972+
29462973
try:
29472974
processed_onion = process_onion_packet(
29482975
onion_packet,
2949-
our_onion_private_key=self.privkey,
2976+
our_onion_private_key=privkey,
29502977
associated_data=payment_hash,
29512978
is_trampoline=is_trampoline)
29522979
except UnsupportedOnionPacketVersion:

electrum/lnutil.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1913,16 +1913,18 @@ class UpdateAddHtlc:
19131913
cltv_abs: int
19141914
htlc_id: Optional[int] = dataclasses.field(default=None)
19151915
timestamp: int = dataclasses.field(default_factory=lambda: int(time.time()))
1916+
blinding: bytes = None
19161917

19171918
@staticmethod
19181919
@stored_in('adds', tuple)
1919-
def from_tuple(amount_msat, rhash, cltv_abs, htlc_id, timestamp) -> 'UpdateAddHtlc':
1920+
def from_tuple(amount_msat, rhash, cltv_abs, htlc_id, timestamp, blinding = None) -> 'UpdateAddHtlc':
19201921
return UpdateAddHtlc(
19211922
amount_msat=amount_msat,
19221923
payment_hash=bytes.fromhex(rhash),
19231924
cltv_abs=cltv_abs,
19241925
htlc_id=htlc_id,
1925-
timestamp=timestamp)
1926+
timestamp=timestamp,
1927+
blinding=None if blinding is None else bytes.fromhex(blinding))
19261928

19271929
def to_json(self):
19281930
self._validate()

electrum/lnwire/peer_wire.csv

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,9 @@ msgdata,update_add_htlc,amount_msat,u64,
116116
msgdata,update_add_htlc,payment_hash,sha256,
117117
msgdata,update_add_htlc,cltv_expiry,u32,
118118
msgdata,update_add_htlc,onion_routing_packet,byte,1366
119-
tlvtype,update_add_htlc_tlvs,blinding_point,0
120-
tlvdata,update_add_htlc_tlvs,blinding_point,blinding,point,
119+
msgdata,update_add_htlc,tlvs,update_add_htlc_tlvs,
120+
tlvtype,update_add_htlc_tlvs,blinded_path,0
121+
tlvdata,update_add_htlc_tlvs,blinded_path,path_key,point,
121122
msgtype,update_fulfill_htlc,130
122123
msgdata,update_fulfill_htlc,channel_id,channel_id,
123124
msgdata,update_fulfill_htlc,id,u64,

0 commit comments

Comments
 (0)