diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py index e8102891e..e563382e9 100644 --- a/electrum/lnpeer.py +++ b/electrum/lnpeer.py @@ -44,7 +44,8 @@ from .lnutil import (Outpoint, LocalConfig, RECEIVED, UpdateAddHtlc, ChannelConf ln_compare_features, MIN_FINAL_CLTV_DELTA_ACCEPTED, RemoteMisbehaving, ShortChannelID, IncompatibleLightningFeatures, derive_payment_secret_from_payment_preimage, - ChannelType, LNProtocolWarning, validate_features, IncompatibleOrInsaneFeatures, NoPathFound) + ChannelType, LNProtocolWarning, validate_features, + IncompatibleOrInsaneFeatures, FeeBudgetExceeded) from .lnutil import FeeUpdate, channel_id_from_funding_tx, PaymentFeeBudget from .lnutil import serialize_htlc_key, Keypair from .lntransport import LNTransport, LNTransportBase, LightningPeerConnectionClosed, HandshakeFailed @@ -2063,11 +2064,8 @@ class Peer(Logger, EventListener): ) except OnionRoutingFailure as e: raise - except NoPathFound as e: - failure_code = (OnionFailureCode.TRAMPOLINE_FEE_INSUFFICIENT - if e.maybe_fee_related - else OnionFailureCode.UNKNOWN_NEXT_PEER) - raise OnionRoutingFailure(code=failure_code, data=b'') + except FeeBudgetExceeded: + raise OnionRoutingFailure(code=OnionFailureCode.TRAMPOLINE_FEE_INSUFFICIENT, data=b'') except PaymentFailure as e: self.logger.debug( f"maybe_forward_trampoline. PaymentFailure for {payment_hash.hex()=}, {payment_secret.hex()=}: {e!r}") diff --git a/electrum/lnutil.py b/electrum/lnutil.py index 288035816..1ad1703ae 100644 --- a/electrum/lnutil.py +++ b/electrum/lnutil.py @@ -450,13 +450,11 @@ class InvalidGossipMsg(Exception): class PaymentFailure(UserFacingException): pass class NoPathFound(PaymentFailure): - def __init__(self, message: Optional[str] = None, *, maybe_fee_related: Optional[bool] = False): - self.maybe_fee_related = maybe_fee_related - self._message = message - super().__init__(message) - def __str__(self): - return self._message or _('No path found') + return _('No path found') +class FeeBudgetExceeded(PaymentFailure): + def __str__(self): + return _('Fee budget exceeded') class LNProtocolError(Exception): diff --git a/electrum/lnworker.py b/electrum/lnworker.py index bf104e570..0a28371fb 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -67,7 +67,7 @@ from .lnutil import (Outpoint, NUM_MAX_EDGES_IN_PAYMENT_PATH, SENT, RECEIVED, HTLCOwner, UpdateAddHtlc, Direction, LnFeatures, ShortChannelID, HtlcLog, derive_payment_secret_from_payment_preimage, - NoPathFound, InvalidGossipMsg) + NoPathFound, InvalidGossipMsg, FeeBudgetExceeded) from .lnutil import ln_compare_features, IncompatibleLightningFeatures, PaymentFeeBudget from .transaction import PartialTxOutput, PartialTransaction, PartialTxInput from .lnonion import decode_onion_error, OnionFailureCode, OnionRoutingFailure, OnionPacket @@ -1880,7 +1880,7 @@ class LNWallet(LNWorker): and mpp is supported by the receiver, we will split the payment.""" trampoline_features = LnFeatures.VAR_ONION_OPT local_height = self.network.get_local_height() - fee_related_error = False + fee_related_error = None # type: Optional[FeeBudgetExceeded] if channels: my_active_channels = channels else: @@ -1973,7 +1973,7 @@ class LNWallet(LNWorker): if per_trampoline_fees != 0: e = 'not enough margin to pay trampoline fee' self.logger.info(e) - raise NoPathFound(e, maybe_fee_related=True) + raise FeeBudgetExceeded(e) else: # We atomically loop through a split configuration. If there was # a failure to find a path for a single part, we try the next configuration @@ -2004,13 +2004,17 @@ class LNWallet(LNWorker): trampoline_route=None, ) routes.append((shi, paysession.min_final_cltv_delta, fwd_trampoline_onion)) - except NoPathFound as e: - fee_related_error = e.maybe_fee_related + except NoPathFound: + continue + except FeeBudgetExceeded as e: + fee_related_error = e continue for route in routes: yield route return - raise NoPathFound(maybe_fee_related=fee_related_error) + if fee_related_error is not None: + raise fee_related_error + raise NoPathFound() @profiler def create_route_for_single_htlc( @@ -2082,7 +2086,7 @@ class LNWallet(LNWorker): route, budget=budget, amount_msat_for_dest=amount_msat, cltv_delta_for_dest=min_final_cltv_delta, ): self.logger.info(f"rejecting route (exceeds budget): {route=}. {budget=}") - raise NoPathFound(maybe_fee_related=True) + raise FeeBudgetExceeded() assert len(route) > 0 if route[-1].end_node != invoice_pubkey: raise LNPathInconsistent("last node_id != invoice pubkey") diff --git a/electrum/trampoline.py b/electrum/trampoline.py index eca725fdf..04777406d 100644 --- a/electrum/trampoline.py +++ b/electrum/trampoline.py @@ -3,7 +3,7 @@ import os import random from typing import Mapping, DefaultDict, Tuple, Optional, Dict, List, Iterable, Sequence, Set, Any -from .lnutil import LnFeatures, PaymentFeeBudget +from .lnutil import LnFeatures, PaymentFeeBudget, FeeBudgetExceeded from .lnonion import calc_hops_data_for_payment, new_onion_packet, OnionPacket from .lnrouter import RouteEdge, TrampolineEdge, LNPaymentRoute, is_route_within_budget, LNPaymentTRoute from .lnutil import NoPathFound @@ -161,7 +161,7 @@ def _allocate_fee_along_route( assert trampoline_fee_level > 0 MAX_LEVEL = 6 if trampoline_fee_level > MAX_LEVEL: - raise NoPathFound("highest trampoline fee level reached", maybe_fee_related=True) + raise FeeBudgetExceeded("highest trampoline fee level reached") budget_to_use = budget.fee_msat // (2 ** (MAX_LEVEL - trampoline_fee_level)) _logger.debug(f"_allocate_fee_along_route(). {trampoline_fee_level=}, {budget.fee_msat=}, {budget_to_use=}") # replace placeholder fees @@ -264,7 +264,7 @@ def create_trampoline_route( amount_msat_for_dest=amount_msat, cltv_delta_for_dest=min_final_cltv_delta, ): - raise NoPathFound("route exceeds budget", maybe_fee_related=True) + raise FeeBudgetExceeded(f"route exceeds budget: budget: {budget}") return route