Browse Source

split code in htlc_switch:

- raise OnionRoutingFailure whenever we want to fail a htlc
 - catch that exception in htlc_switch
 - this will avoid code duplication in the case of trampoline
patch-4
ThomasV 4 years ago
parent
commit
87a080d30e
  1. 13
      electrum/lnchannel.py
  2. 13
      electrum/lnonion.py
  3. 182
      electrum/lnpeer.py
  4. 6
      electrum/lnworker.py

13
electrum/lnchannel.py

@ -41,7 +41,7 @@ from .bitcoin import redeem_script_to_address
from .crypto import sha256, sha256d
from .transaction import Transaction, PartialTransaction, TxInput
from .logging import Logger
from .lnonion import decode_onion_error, OnionFailureCode, OnionRoutingFailureMessage
from .lnonion import decode_onion_error, OnionFailureCode, OnionRoutingFailure
from . import lnutil
from .lnutil import (Outpoint, LocalConfig, RemoteConfig, Keypair, OnlyPubkeyKeypair, ChannelConstraints,
get_per_commitment_secret_from_seed, secret_to_pubkey, derive_privkey, make_closing_tx,
@ -528,7 +528,7 @@ class Channel(AbstractChannel):
self._chan_ann_without_sigs = None # type: Optional[bytes]
self.revocation_store = RevocationStore(state["revocation_store"])
self._can_send_ctx_updates = True # type: bool
self._receive_fail_reasons = {} # type: Dict[int, (bytes, OnionRoutingFailureMessage)]
self._receive_fail_reasons = {} # type: Dict[int, (bytes, OnionRoutingFailure)]
self._ignore_max_htlc_value = False # used in tests
def is_initiator(self):
@ -1008,8 +1008,7 @@ class Channel(AbstractChannel):
self,
htlc_id: int,
error_bytes: Optional[bytes],
failure_message: Optional['OnionRoutingFailureMessage'],
):
failure_message: Optional['OnionRoutingFailure']):
error_hex = error_bytes.hex() if error_bytes else None
failure_hex = failure_message.to_bytes().hex() if failure_message else None
self.hm.log['fail_htlc_reasons'][htlc_id] = (error_hex, failure_hex)
@ -1017,7 +1016,7 @@ class Channel(AbstractChannel):
def pop_fail_htlc_reason(self, htlc_id):
error_hex, failure_hex = self.hm.log['fail_htlc_reasons'].pop(htlc_id, (None, None))
error_bytes = bytes.fromhex(error_hex) if error_hex else None
failure_message = OnionRoutingFailureMessage.from_bytes(bytes.fromhex(failure_hex)) if failure_hex else None
failure_message = OnionRoutingFailure.from_bytes(bytes.fromhex(failure_hex)) if failure_hex else None
return error_bytes, failure_message
def extract_preimage_from_htlc_txin(self, txin: TxInput) -> None:
@ -1238,7 +1237,7 @@ class Channel(AbstractChannel):
return htlc.payment_hash
def decode_onion_error(self, reason: bytes, route: Sequence['RouteEdge'],
htlc_id: int) -> Tuple[OnionRoutingFailureMessage, int]:
htlc_id: int) -> Tuple[OnionRoutingFailure, int]:
failure_msg, sender_idx = decode_onion_error(
reason,
[x.node_id for x in route],
@ -1267,7 +1266,7 @@ class Channel(AbstractChannel):
def receive_fail_htlc(self, htlc_id: int, *,
error_bytes: Optional[bytes],
reason: Optional[OnionRoutingFailureMessage] = None) -> None:
reason: Optional[OnionRoutingFailure] = None) -> None:
"""Fail a pending offered HTLC.
Action must be initiated by REMOTE.
"""

13
electrum/lnonion.py

@ -381,7 +381,7 @@ def process_onion_packet(onion_packet: OnionPacket, associated_data: bytes,
class FailedToDecodeOnionError(Exception): pass
class OnionRoutingFailureMessage:
class OnionRoutingFailure(Exception):
def __init__(self, code: int, data: bytes):
self.code = code
@ -403,15 +403,14 @@ class OnionRoutingFailureMessage:
except ValueError:
pass # uknown failure code
failure_data = failure_msg[2:]
return OnionRoutingFailureMessage(failure_code, failure_data)
return OnionRoutingFailure(failure_code, failure_data)
def code_name(self) -> str:
if isinstance(self.code, OnionFailureCode):
return str(self.code.name)
return f"Unknown error ({self.code!r})"
def construct_onion_error(reason: OnionRoutingFailureMessage,
def construct_onion_error(reason: OnionRoutingFailure,
onion_packet: OnionPacket,
our_onion_private_key: bytes) -> bytes:
# create payload
@ -453,19 +452,19 @@ def _decode_onion_error(error_packet: bytes, payment_path_pubkeys: Sequence[byte
def decode_onion_error(error_packet: bytes, payment_path_pubkeys: Sequence[bytes],
session_key: bytes) -> (OnionRoutingFailureMessage, int):
session_key: bytes) -> (OnionRoutingFailure, int):
"""Returns the failure message, and the index of the sender of the error."""
decrypted_error, sender_index = _decode_onion_error(error_packet, payment_path_pubkeys, session_key)
failure_msg = get_failure_msg_from_onion_error(decrypted_error)
return failure_msg, sender_index
def get_failure_msg_from_onion_error(decrypted_error_packet: bytes) -> OnionRoutingFailureMessage:
def get_failure_msg_from_onion_error(decrypted_error_packet: bytes) -> OnionRoutingFailure:
# get failure_msg bytes from error packet
failure_len = int.from_bytes(decrypted_error_packet[32:34], byteorder='big')
failure_msg = decrypted_error_packet[34:34+failure_len]
# create failure message object
return OnionRoutingFailureMessage.from_bytes(failure_msg)
return OnionRoutingFailure.from_bytes(failure_msg)

182
electrum/lnpeer.py

@ -25,7 +25,7 @@ from . import transaction
from .transaction import PartialTxOutput, match_script_against_template
from .logging import Logger
from .lnonion import (new_onion_packet, OnionFailureCode, calc_hops_data_for_payment,
process_onion_packet, OnionPacket, construct_onion_error, OnionRoutingFailureMessage,
process_onion_packet, OnionPacket, construct_onion_error, OnionRoutingFailure,
ProcessedOnionPacket, UnsupportedOnionPacketVersion, InvalidOnionMac, InvalidOnionPubkey,
OnionFailureCodeMetaFlag)
from .lnchannel import Channel, RevokeAndAck, RemoteCtnTooFarInFuture, ChannelState, PeerState
@ -1267,7 +1267,7 @@ class Peer(Logger):
if failure_code & OnionFailureCodeMetaFlag.BADONION == 0:
asyncio.ensure_future(self.lnworker.try_force_closing(chan.channel_id))
raise RemoteMisbehaving(f"received update_fail_malformed_htlc with unexpected failure code: {failure_code}")
reason = OnionRoutingFailureMessage(code=failure_code, data=payload["sha256_of_onion"])
reason = OnionRoutingFailure(code=failure_code, data=payload["sha256_of_onion"])
chan.receive_fail_htlc(htlc_id, error_bytes=None, reason=reason)
self.maybe_send_commitment(chan)
@ -1295,56 +1295,56 @@ class Peer(Logger):
def maybe_forward_htlc(self, chan: Channel, htlc: UpdateAddHtlc, *,
onion_packet: OnionPacket, processed_onion: ProcessedOnionPacket
) -> Tuple[Optional[bytes], Optional[int], Optional[OnionRoutingFailureMessage]]:
) -> Tuple[Optional[bytes], Optional[int], Optional[OnionRoutingFailure]]:
# Forward HTLC
# FIXME: there are critical safety checks MISSING here
forwarding_enabled = self.network.config.get('lightning_forward_payments', False)
if not forwarding_enabled:
self.logger.info(f"forwarding is disabled. failing htlc.")
return None, None, OnionRoutingFailureMessage(code=OnionFailureCode.PERMANENT_CHANNEL_FAILURE, data=b'')
raise OnionRoutingFailure(code=OnionFailureCode.PERMANENT_CHANNEL_FAILURE, data=b'')
chain = self.network.blockchain()
if chain.is_tip_stale():
return None, None, OnionRoutingFailureMessage(code=OnionFailureCode.TEMPORARY_NODE_FAILURE, data=b'')
raise OnionRoutingFailure(code=OnionFailureCode.TEMPORARY_NODE_FAILURE, data=b'')
try:
next_chan_scid = processed_onion.hop_data.payload["short_channel_id"]["short_channel_id"]
except:
return None, None, OnionRoutingFailureMessage(code=OnionFailureCode.INVALID_ONION_PAYLOAD, data=b'\x00\x00\x00')
raise OnionRoutingFailure(code=OnionFailureCode.INVALID_ONION_PAYLOAD, data=b'\x00\x00\x00')
next_chan = self.lnworker.get_channel_by_short_id(next_chan_scid)
local_height = chain.height()
if next_chan is None:
self.logger.info(f"cannot forward htlc. cannot find next_chan {next_chan_scid}")
return None, None, OnionRoutingFailureMessage(code=OnionFailureCode.UNKNOWN_NEXT_PEER, data=b'')
raise OnionRoutingFailure(code=OnionFailureCode.UNKNOWN_NEXT_PEER, data=b'')
outgoing_chan_upd = next_chan.get_outgoing_gossip_channel_update()[2:]
outgoing_chan_upd_len = len(outgoing_chan_upd).to_bytes(2, byteorder="big")
if not next_chan.can_send_update_add_htlc():
self.logger.info(f"cannot forward htlc. next_chan {next_chan_scid} cannot send ctx updates. "
f"chan state {next_chan.get_state()!r}, peer state: {next_chan.peer_state!r}")
data = outgoing_chan_upd_len + outgoing_chan_upd
return None, None, OnionRoutingFailureMessage(code=OnionFailureCode.TEMPORARY_CHANNEL_FAILURE, data=data)
raise OnionRoutingFailure(code=OnionFailureCode.TEMPORARY_CHANNEL_FAILURE, data=data)
try:
next_cltv_expiry = processed_onion.hop_data.payload["outgoing_cltv_value"]["outgoing_cltv_value"]
except:
return None, None, OnionRoutingFailureMessage(code=OnionFailureCode.INVALID_ONION_PAYLOAD, data=b'\x00\x00\x00')
raise OnionRoutingFailure(code=OnionFailureCode.INVALID_ONION_PAYLOAD, data=b'\x00\x00\x00')
if htlc.cltv_expiry - next_cltv_expiry < NBLOCK_OUR_CLTV_EXPIRY_DELTA:
data = htlc.cltv_expiry.to_bytes(4, byteorder="big") + outgoing_chan_upd_len + outgoing_chan_upd
return None, None, OnionRoutingFailureMessage(code=OnionFailureCode.INCORRECT_CLTV_EXPIRY, data=data)
raise OnionRoutingFailure(code=OnionFailureCode.INCORRECT_CLTV_EXPIRY, data=data)
if htlc.cltv_expiry - lnutil.MIN_FINAL_CLTV_EXPIRY_ACCEPTED <= local_height \
or next_cltv_expiry <= local_height:
data = outgoing_chan_upd_len + outgoing_chan_upd
return None, None, OnionRoutingFailureMessage(code=OnionFailureCode.EXPIRY_TOO_SOON, data=data)
raise OnionRoutingFailure(code=OnionFailureCode.EXPIRY_TOO_SOON, data=data)
if max(htlc.cltv_expiry, next_cltv_expiry) > local_height + lnutil.NBLOCK_CLTV_EXPIRY_TOO_FAR_INTO_FUTURE:
return None, None, OnionRoutingFailureMessage(code=OnionFailureCode.EXPIRY_TOO_FAR, data=b'')
raise OnionRoutingFailure(code=OnionFailureCode.EXPIRY_TOO_FAR, data=b'')
try:
next_amount_msat_htlc = processed_onion.hop_data.payload["amt_to_forward"]["amt_to_forward"]
except:
return None, None, OnionRoutingFailureMessage(code=OnionFailureCode.INVALID_ONION_PAYLOAD, data=b'\x00\x00\x00')
raise OnionRoutingFailure(code=OnionFailureCode.INVALID_ONION_PAYLOAD, data=b'\x00\x00\x00')
forwarding_fees = fee_for_edge_msat(
forwarded_amount_msat=next_amount_msat_htlc,
fee_base_msat=lnutil.OUR_FEE_BASE_MSAT,
fee_proportional_millionths=lnutil.OUR_FEE_PROPORTIONAL_MILLIONTHS)
if htlc.amount_msat - next_amount_msat_htlc < forwarding_fees:
data = next_amount_msat_htlc.to_bytes(8, byteorder="big") + outgoing_chan_upd_len + outgoing_chan_upd
return None, None, OnionRoutingFailureMessage(code=OnionFailureCode.FEE_INSUFFICIENT, data=data)
raise OnionRoutingFailure(code=OnionFailureCode.FEE_INSUFFICIENT, data=data)
self.logger.info(f'forwarding htlc to {next_chan.node_id}')
next_htlc = UpdateAddHtlc(
amount_msat=next_amount_msat_htlc,
@ -1366,19 +1366,18 @@ class Peer(Logger):
except BaseException as e:
self.logger.info(f"failed to forward htlc: error sending message. {e}")
data = outgoing_chan_upd_len + outgoing_chan_upd
return None, None, OnionRoutingFailureMessage(code=OnionFailureCode.TEMPORARY_CHANNEL_FAILURE, data=data)
return next_chan_scid, next_htlc.htlc_id, None
raise OnionRoutingFailure(code=OnionFailureCode.TEMPORARY_CHANNEL_FAILURE, data=data)
return next_chan_scid, next_htlc.htlc_id
def maybe_fulfill_htlc(
self, *,
chan: Channel,
htlc: UpdateAddHtlc,
processed_onion: ProcessedOnionPacket) -> Tuple[Optional[bytes], Optional[OnionRoutingFailureMessage]]:
processed_onion: ProcessedOnionPacket) -> Tuple[Optional[bytes], Optional[OnionRoutingFailure]]:
info = self.lnworker.get_payment_info(htlc.payment_hash)
if info is None:
reason = OnionRoutingFailureMessage(code=OnionFailureCode.INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS, data=b'')
return None, reason
raise OnionRoutingFailure(code=OnionFailureCode.INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS, data=b'')
preimage = self.lnworker.get_preimage(htlc.payment_hash)
try:
payment_secret_from_onion = processed_onion.hop_data.payload["payment_data"]["payment_secret"]
@ -1386,59 +1385,49 @@ class Peer(Logger):
pass # skip
else:
if payment_secret_from_onion != derive_payment_secret_from_payment_preimage(preimage):
reason = OnionRoutingFailureMessage(code=OnionFailureCode.INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS, data=b'')
return None, reason
raise OnionRoutingFailure(code=OnionFailureCode.INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS, data=b'')
expected_received_msat = info.amount_msat
# Check that our blockchain tip is sufficiently recent so that we have an approx idea of the height.
# We should not release the preimage for an HTLC that its sender could already time out as
# then they might try to force-close and it becomes a race.
chain = self.network.blockchain()
if chain.is_tip_stale():
reason = OnionRoutingFailureMessage(code=OnionFailureCode.TEMPORARY_NODE_FAILURE, data=b'')
return None, reason
raise OnionRoutingFailure(code=OnionFailureCode.TEMPORARY_NODE_FAILURE, data=b'')
local_height = chain.height()
if local_height + MIN_FINAL_CLTV_EXPIRY_ACCEPTED > htlc.cltv_expiry:
reason = OnionRoutingFailureMessage(code=OnionFailureCode.FINAL_EXPIRY_TOO_SOON, data=b'')
return None, reason
raise OnionRoutingFailure(code=OnionFailureCode.FINAL_EXPIRY_TOO_SOON, data=b'')
try:
cltv_from_onion = processed_onion.hop_data.payload["outgoing_cltv_value"]["outgoing_cltv_value"]
except:
reason = OnionRoutingFailureMessage(code=OnionFailureCode.INVALID_ONION_PAYLOAD, data=b'\x00\x00\x00')
return None, reason
raise OnionRoutingFailure(code=OnionFailureCode.INVALID_ONION_PAYLOAD, data=b'\x00\x00\x00')
if cltv_from_onion != htlc.cltv_expiry:
reason = OnionRoutingFailureMessage(
raise OnionRoutingFailure(
code=OnionFailureCode.FINAL_INCORRECT_CLTV_EXPIRY,
data=htlc.cltv_expiry.to_bytes(4, byteorder="big"))
return None, reason
try:
amt_to_forward = processed_onion.hop_data.payload["amt_to_forward"]["amt_to_forward"]
except:
reason = OnionRoutingFailureMessage(code=OnionFailureCode.INVALID_ONION_PAYLOAD, data=b'\x00\x00\x00')
return None, reason
raise OnionRoutingFailure(code=OnionFailureCode.INVALID_ONION_PAYLOAD, data=b'\x00\x00\x00')
try:
total_msat = processed_onion.hop_data.payload["payment_data"]["total_msat"]
except:
total_msat = amt_to_forward # fall back to "amt_to_forward"
if amt_to_forward != htlc.amount_msat:
reason = OnionRoutingFailureMessage(
raise OnionRoutingFailure(
code=OnionFailureCode.FINAL_INCORRECT_HTLC_AMOUNT,
data=total_msat.to_bytes(8, byteorder="big"))
return None, reason
if expected_received_msat is None:
return preimage, None
return preimage
if not (expected_received_msat <= total_msat <= 2 * expected_received_msat):
reason = OnionRoutingFailureMessage(code=OnionFailureCode.INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS, data=b'')
return None, reason
raise OnionRoutingFailure(code=OnionFailureCode.INCORRECT_OR_UNKNOWN_PAYMENT_DETAILS, data=b'')
accepted, expired = self.lnworker.htlc_received(chan.short_channel_id, htlc, expected_received_msat)
if accepted:
return preimage, None
return preimage
elif expired:
reason = OnionRoutingFailureMessage(code=OnionFailureCode.MPP_TIMEOUT, data=b'')
return None, reason
raise OnionRoutingFailure(code=OnionFailureCode.MPP_TIMEOUT, data=b'')
else:
# waiting for more htlcs
return None, None
return None
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}")
@ -1461,7 +1450,7 @@ class Peer(Logger):
len=len(error_bytes),
reason=error_bytes)
def fail_malformed_htlc(self, *, chan: Channel, htlc_id: int, reason: OnionRoutingFailureMessage):
def fail_malformed_htlc(self, *, chan: Channel, htlc_id: int, reason: OnionRoutingFailure):
self.logger.info(f"fail_malformed_htlc. chan {chan.short_channel_id}. htlc_id {htlc_id}.")
assert chan.can_send_ctx_updates(), f"cannot send updates: {chan.short_channel_id}"
chan.fail_htlc(htlc_id)
@ -1679,60 +1668,29 @@ class Peer(Logger):
for htlc_id, (local_ctn, remote_ctn, onion_packet_hex, forwarding_info) in unfulfilled.items():
if not chan.hm.is_add_htlc_irrevocably_committed_yet(htlc_proposer=REMOTE, htlc_id=htlc_id):
continue
#chan.logger.info(f'found unfulfilled htlc: {htlc_id}')
htlc = chan.hm.get_htlc_by_id(REMOTE, htlc_id)
payment_hash = htlc.payment_hash
error_reason = None # type: Optional[OnionRoutingFailureMessage]
error_reason = None # type: Optional[OnionRoutingFailure]
error_bytes = None # type: Optional[bytes]
preimage = None
fw_info = None
onion_packet_bytes = bytes.fromhex(onion_packet_hex)
onion_packet = None
try:
onion_packet = OnionPacket.from_bytes(onion_packet_bytes)
processed_onion = process_onion_packet(onion_packet, associated_data=payment_hash, our_onion_private_key=self.privkey)
except UnsupportedOnionPacketVersion:
error_reason = OnionRoutingFailureMessage(code=OnionFailureCode.INVALID_ONION_VERSION, data=sha256(onion_packet_bytes))
except InvalidOnionPubkey:
error_reason = OnionRoutingFailureMessage(code=OnionFailureCode.INVALID_ONION_KEY, data=sha256(onion_packet_bytes))
except InvalidOnionMac:
error_reason = OnionRoutingFailureMessage(code=OnionFailureCode.INVALID_ONION_HMAC, data=sha256(onion_packet_bytes))
except Exception as e:
self.logger.info(f"error processing onion packet: {e!r}")
error_reason = OnionRoutingFailureMessage(code=OnionFailureCode.INVALID_ONION_VERSION, data=sha256(onion_packet_bytes))
except OnionRoutingFailure as e:
error_reason = e
else:
if self.network.config.get('test_fail_malformed_htlc'):
error_reason = OnionRoutingFailureMessage(code=OnionFailureCode.INVALID_ONION_VERSION, data=sha256(onion_packet_bytes))
if self.network.config.get('test_fail_htlcs_with_temp_node_failure'):
error_reason = OnionRoutingFailureMessage(code=OnionFailureCode.TEMPORARY_NODE_FAILURE, data=b'')
if not error_reason:
if processed_onion.are_we_final:
preimage, error_reason = self.maybe_fulfill_htlc(
chan=chan,
htlc=htlc,
processed_onion=processed_onion)
elif not forwarding_info:
next_chan_id, next_htlc_id, error_reason = self.maybe_forward_htlc(
chan=chan,
htlc=htlc,
onion_packet=onion_packet,
processed_onion=processed_onion)
if next_chan_id:
fw_info = (next_chan_id.hex(), next_htlc_id)
unfulfilled[htlc_id] = local_ctn, remote_ctn, onion_packet_hex, fw_info
else:
preimage = self.lnworker.get_preimage(payment_hash)
next_chan_id_hex, htlc_id = forwarding_info
next_chan = self.lnworker.get_channel_by_short_id(bytes.fromhex(next_chan_id_hex))
if next_chan:
error_bytes, error_reason = next_chan.pop_fail_htlc_reason(htlc_id)
try:
preimage, fw_info, error_bytes = self.process_unfulfilled_htlc(chan, htlc_id, htlc, forwarding_info, onion_packet_bytes, onion_packet)
except OnionRoutingFailure as e:
error_bytes = construct_onion_error(e, onion_packet, our_onion_private_key=self.privkey)
if fw_info:
unfulfilled[htlc_id] = local_ctn, remote_ctn, onion_packet_hex, fw_info
elif preimage or error_reason or error_bytes:
if preimage:
await self.lnworker.enable_htlc_settle.wait()
self.fulfill_htlc(chan, htlc.htlc_id, preimage)
done.add(htlc_id)
if error_reason or error_bytes:
if onion_packet and error_reason:
error_bytes = construct_onion_error(error_reason, onion_packet, our_onion_private_key=self.privkey)
if error_bytes:
elif error_bytes:
self.fail_htlc(
chan=chan,
htlc_id=htlc.htlc_id,
@ -1746,3 +1704,57 @@ class Peer(Logger):
# cleanup
for htlc_id in done:
unfulfilled.pop(htlc_id)
def process_unfulfilled_htlc(self, chan, htlc_id, htlc, forwarding_info, onion_packet_bytes, onion_packet):
"""
returns either preimage or fw_info or error_bytes or (None, None, None)
raise an OnionRoutingFailure if we need to fail the htlc
"""
payment_hash = htlc.payment_hash
processed_onion = self.process_onion_packet(onion_packet, payment_hash, onion_packet_bytes)
if processed_onion.are_we_final:
preimage = self.maybe_fulfill_htlc(
chan=chan,
htlc=htlc,
processed_onion=processed_onion)
elif not forwarding_info:
next_chan_id, next_htlc_id = self.maybe_forward_htlc(
chan=chan,
htlc=htlc,
onion_packet=onion_packet,
processed_onion=processed_onion)
if next_chan_id:
fw_info = (next_chan_id.hex(), next_htlc_id)
return None, fw_info, None
else:
preimage = self.lnworker.get_preimage(payment_hash)
next_chan_id_hex, htlc_id = forwarding_info
next_chan = self.lnworker.get_channel_by_short_id(bytes.fromhex(next_chan_id_hex))
if next_chan:
error_bytes, error_reason = next_chan.pop_fail_htlc_reason(htlc_id)
if error_bytes:
return None, None, error_bytes
if error_reason:
raise error_reason
if preimage:
return preimage, None, None
return None, None, None
def process_onion_packet(self, onion_packet, payment_hash, onion_packet_bytes):
failure_data = sha256(onion_packet_bytes)
try:
processed_onion = process_onion_packet(onion_packet, associated_data=payment_hash, our_onion_private_key=self.privkey)
except UnsupportedOnionPacketVersion:
raise OnionRoutingFailure(code=OnionFailureCode.INVALID_ONION_VERSION, data=failure_data)
except InvalidOnionPubkey:
raise OnionRoutingFailure(code=OnionFailureCode.INVALID_ONION_KEY, data=failure_data)
except InvalidOnionMac:
raise OnionRoutingFailure(code=OnionFailureCode.INVALID_ONION_HMAC, data=failure_data)
except Exception as e:
self.logger.info(f"error processing onion packet: {e!r}")
raise OnionRoutingFailure(code=OnionFailureCode.INVALID_ONION_VERSION, data=failure_data)
if self.network.config.get('test_fail_malformed_htlc'):
raise OnionRoutingFailure(code=OnionFailureCode.INVALID_ONION_VERSION, data=failure_data)
if self.network.config.get('test_fail_htlcs_with_temp_node_failure'):
raise OnionRoutingFailure(code=OnionFailureCode.TEMPORARY_NODE_FAILURE, data=b'')
return processed_onion

6
electrum/lnworker.py

@ -59,7 +59,7 @@ from .lnutil import (Outpoint, LNPeerAddr,
HtlcLog, derive_payment_secret_from_payment_preimage)
from .lnutil import ln_dummy_address, ln_compare_features, IncompatibleLightningFeatures
from .transaction import PartialTxOutput, PartialTransaction, PartialTxInput
from .lnonion import OnionFailureCode, process_onion_packet, OnionPacket, OnionRoutingFailureMessage
from .lnonion import OnionFailureCode, process_onion_packet, OnionPacket, OnionRoutingFailure
from .lnmsg import decode_msg
from .i18n import _
from .lnrouter import (RouteEdge, LNPaymentRoute, LNPaymentPath, is_route_sane_to_use,
@ -1379,7 +1379,7 @@ class LNWallet(LNWorker):
htlc_id: int,
amount_msat:int,
error_bytes: Optional[bytes],
failure_message: Optional['OnionRoutingFailureMessage']):
failure_message: Optional['OnionRoutingFailure']):
route = self.htlc_routes.get((payment_hash, chan.short_channel_id, htlc_id))
if not route:
@ -1392,7 +1392,7 @@ class LNWallet(LNWorker):
failure_message, sender_idx = chan.decode_onion_error(error_bytes, route, htlc_id)
except Exception as e:
sender_idx = None
failure_message = OnionRoutingFailureMessage(-1, str(e))
failure_message = OnionRoutingFailure(-1, str(e))
else:
# probably got "update_fail_malformed_htlc". well... who to penalise now?
assert failure_message is not None

Loading…
Cancel
Save