trampoline: fix off-by-one confusion of fees
The convention is that edges (start_node -> edge_node) store the policy/fees for the *start_node*. This is what the non-trampoline edges were already using (for a long time), but the trampoline ones were off-by-one (policy was for end_node), which was then worked around in multiple places, to correct for... i.e. I think because of all the workarounds, there was no actual bug, but it was just very confusing. Also note that the prior usage of trampoline edges would not work if we (sender) were not directly connected to a TF (trampoline-forwarder) but had extra edges in the route to even get to the first TF. Having the policy corresponding to the start_node of the edge would work even in that case.
This commit is contained in:
@@ -73,10 +73,10 @@ class PathEdge:
|
||||
|
||||
@attr.s
|
||||
class RouteEdge(PathEdge):
|
||||
fee_base_msat = attr.ib(type=int, kw_only=True)
|
||||
fee_proportional_millionths = attr.ib(type=int, kw_only=True)
|
||||
cltv_delta = attr.ib(type=int, kw_only=True)
|
||||
node_features = attr.ib(type=int, kw_only=True, repr=lambda val: str(int(val))) # note: for end node!
|
||||
fee_base_msat = attr.ib(type=int, kw_only=True) # for start_node
|
||||
fee_proportional_millionths = attr.ib(type=int, kw_only=True) # for start_node
|
||||
cltv_delta = attr.ib(type=int, kw_only=True) # for start_node
|
||||
node_features = attr.ib(type=int, kw_only=True, repr=lambda val: str(int(val))) # note: for end_node!
|
||||
|
||||
def fee_for_edge(self, amount_msat: int) -> int:
|
||||
return fee_for_edge_msat(forwarded_amount_msat=amount_msat,
|
||||
@@ -87,7 +87,7 @@ class RouteEdge(PathEdge):
|
||||
def from_channel_policy(
|
||||
cls,
|
||||
*,
|
||||
channel_policy: 'Policy',
|
||||
channel_policy: 'Policy', # for start_node
|
||||
short_channel_id: bytes,
|
||||
start_node: bytes,
|
||||
end_node: bytes,
|
||||
@@ -138,26 +138,26 @@ LNPaymentRoute = Sequence[RouteEdge]
|
||||
LNPaymentTRoute = Sequence[TrampolineEdge]
|
||||
|
||||
|
||||
def is_route_sane_to_use(route: LNPaymentRoute, invoice_amount_msat: int, min_final_cltv_delta: int) -> bool:
|
||||
def is_route_sane_to_use(route: LNPaymentRoute, *, amount_msat_for_dest: int, cltv_delta_for_dest: int) -> bool:
|
||||
"""Run some sanity checks on the whole route, before attempting to use it.
|
||||
called when we are paying; so e.g. lower cltv is better
|
||||
"""
|
||||
if len(route) > NUM_MAX_EDGES_IN_PAYMENT_PATH:
|
||||
return False
|
||||
amt = invoice_amount_msat
|
||||
cltv_delta = min_final_cltv_delta
|
||||
amt = amount_msat_for_dest
|
||||
cltv_delta = cltv_delta_for_dest
|
||||
for route_edge in reversed(route[1:]):
|
||||
if not route_edge.is_sane_to_use(amt): return False
|
||||
amt += route_edge.fee_for_edge(amt)
|
||||
cltv_delta += route_edge.cltv_delta
|
||||
total_fee = amt - invoice_amount_msat
|
||||
total_fee = amt - amount_msat_for_dest
|
||||
# TODO revise ad-hoc heuristics
|
||||
if cltv_delta > NBLOCK_CLTV_DELTA_TOO_FAR_INTO_FUTURE:
|
||||
return False
|
||||
# FIXME in case of MPP, the fee checks are done independently for each part,
|
||||
# which is ok for the proportional checks but not for the absolute ones.
|
||||
# This is not that big of a deal though as we don't split into *too many* parts.
|
||||
if not is_fee_sane(total_fee, payment_amount_msat=invoice_amount_msat):
|
||||
if not is_fee_sane(total_fee, payment_amount_msat=amount_msat_for_dest):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user