Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions electrum/lnpeer.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@
ln_compare_features, MIN_FINAL_CLTV_DELTA_ACCEPTED,
RemoteMisbehaving, ShortChannelID,
IncompatibleLightningFeatures, derive_payment_secret_from_payment_preimage,
ChannelType, LNProtocolWarning, validate_features, IncompatibleOrInsaneFeatures)
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
Expand Down Expand Up @@ -2063,11 +2064,12 @@ async def maybe_forward_trampoline(
)
except OnionRoutingFailure as e:
raise
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}")
# FIXME: adapt the error code
raise OnionRoutingFailure(code=OnionFailureCode.TRAMPOLINE_FEE_INSUFFICIENT, data=b'')
raise OnionRoutingFailure(code=OnionFailureCode.UNKNOWN_NEXT_PEER, data=b'')

def _maybe_refuse_to_forward_htlc_that_corresponds_to_payreq_we_created(self, payment_hash: bytes) -> bool:
"""Returns True if the HTLC should be failed.
Expand Down
3 changes: 3 additions & 0 deletions electrum/lnutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,9 @@ class PaymentFailure(UserFacingException): pass
class NoPathFound(PaymentFailure):
def __str__(self):
return _('No path found')
class FeeBudgetExceeded(PaymentFailure):
def __str__(self):
return _('Fee budget exceeded')


class LNProtocolError(Exception):
Expand Down
15 changes: 11 additions & 4 deletions electrum/lnworker.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
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
Expand Down Expand Up @@ -1880,6 +1880,7 @@ async def create_routes_for_payment(
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 = None # type: Optional[FeeBudgetExceeded]
if channels:
my_active_channels = channels
else:
Expand Down Expand Up @@ -1970,8 +1971,9 @@ async def create_routes_for_payment(
)
routes.append((shi, per_trampoline_cltv_delta, trampoline_onion))
if per_trampoline_fees != 0:
self.logger.info('not enough margin to pay trampoline fee')
raise NoPathFound()
e = 'not enough margin to pay trampoline fee'
self.logger.info(e)
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
Expand Down Expand Up @@ -2004,9 +2006,14 @@ async def create_routes_for_payment(
routes.append((shi, paysession.min_final_cltv_delta, fwd_trampoline_onion))
except NoPathFound:
continue
except FeeBudgetExceeded as e:
fee_related_error = e
continue
for route in routes:
yield route
return
if fee_related_error is not None:
raise fee_related_error
raise NoPathFound()

@profiler
Expand Down Expand Up @@ -2079,7 +2086,7 @@ def create_route_for_single_htlc(
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()
raise FeeBudgetExceeded()
assert len(route) > 0
if route[-1].end_node != invoice_pubkey:
raise LNPathInconsistent("last node_id != invoice pubkey")
Expand Down
6 changes: 3 additions & 3 deletions electrum/trampoline.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
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
Expand Down Expand Up @@ -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()
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
Expand Down Expand Up @@ -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")
raise FeeBudgetExceeded(f"route exceeds budget: budget: {budget}")
return route


Expand Down