create_trampoline_route: check that we can pay the amount and the fees, and that the route is sane
This commit is contained in:
@@ -1281,6 +1281,7 @@ class LNWallet(LNWorker):
|
|||||||
|
|
||||||
def create_trampoline_route(
|
def create_trampoline_route(
|
||||||
self, amount_msat:int,
|
self, amount_msat:int,
|
||||||
|
min_cltv_expiry:int,
|
||||||
invoice_pubkey:bytes,
|
invoice_pubkey:bytes,
|
||||||
invoice_features:int,
|
invoice_features:int,
|
||||||
channels: List[Channel],
|
channels: List[Channel],
|
||||||
@@ -1304,86 +1305,92 @@ class LNWallet(LNWorker):
|
|||||||
else:
|
else:
|
||||||
is_legacy = True
|
is_legacy = True
|
||||||
|
|
||||||
# Find a trampoline. We assume we have a direct channel to trampoline
|
|
||||||
for chan in channels:
|
|
||||||
if not self.is_trampoline_peer(chan.node_id):
|
|
||||||
continue
|
|
||||||
if chan.is_active() and chan.can_pay(amount_msat, check_frozen=True):
|
|
||||||
trampoline_short_channel_id = chan.short_channel_id
|
|
||||||
trampoline_node_id = chan.node_id
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise NoPathFound()
|
|
||||||
# use attempt number to decide fee and second trampoline
|
|
||||||
# we need a state with the list of nodes we have not tried
|
|
||||||
# add optional second trampoline
|
|
||||||
trampoline2 = None
|
|
||||||
if is_legacy:
|
|
||||||
for node_id in self.trampoline2_list:
|
|
||||||
if node_id != trampoline_node_id:
|
|
||||||
trampoline2 = node_id
|
|
||||||
break
|
|
||||||
# fee level. the same fee is used for all trampolines
|
# fee level. the same fee is used for all trampolines
|
||||||
if self.trampoline_fee_level < len(TRAMPOLINE_FEES):
|
if self.trampoline_fee_level < len(TRAMPOLINE_FEES):
|
||||||
params = TRAMPOLINE_FEES[self.trampoline_fee_level]
|
params = TRAMPOLINE_FEES[self.trampoline_fee_level]
|
||||||
else:
|
else:
|
||||||
raise NoPathFound()
|
raise NoPathFound()
|
||||||
self.logger.info(f'create route with trampoline: fee_level={self.trampoline_fee_level}, is legacy: {is_legacy}')
|
# Find a trampoline. We assume we have a direct channel to trampoline
|
||||||
self.logger.info(f'first trampoline: {trampoline_node_id.hex()}')
|
for chan in channels:
|
||||||
self.logger.info(f'second trampoline: {trampoline2.hex() if trampoline2 else None}')
|
if not self.is_trampoline_peer(chan.node_id):
|
||||||
self.logger.info(f'params: {params}')
|
continue
|
||||||
# node_features is only used to determine is_tlv
|
trampoline_short_channel_id = chan.short_channel_id
|
||||||
trampoline_features = LnFeatures.VAR_ONION_OPT
|
trampoline_node_id = chan.node_id
|
||||||
# hop to trampoline
|
# use attempt number to decide fee and second trampoline
|
||||||
route = [
|
# we need a state with the list of nodes we have not tried
|
||||||
RouteEdge(
|
# add optional second trampoline
|
||||||
node_id=trampoline_node_id,
|
trampoline2 = None
|
||||||
short_channel_id=trampoline_short_channel_id,
|
if is_legacy:
|
||||||
fee_base_msat=0,
|
for node_id in self.trampoline2_list:
|
||||||
fee_proportional_millionths=0,
|
if node_id != trampoline_node_id:
|
||||||
cltv_expiry_delta=0,
|
trampoline2 = node_id
|
||||||
node_features=trampoline_features)
|
break
|
||||||
]
|
# node_features is only used to determine is_tlv
|
||||||
# trampoline hop
|
trampoline_features = LnFeatures.VAR_ONION_OPT
|
||||||
route.append(
|
# hop to trampoline
|
||||||
TrampolineEdge(
|
route = [
|
||||||
node_id=trampoline_node_id,
|
RouteEdge(
|
||||||
fee_base_msat=params['fee_base_msat'],
|
node_id=trampoline_node_id,
|
||||||
fee_proportional_millionths=params['fee_proportional_millionths'],
|
short_channel_id=trampoline_short_channel_id,
|
||||||
cltv_expiry_delta=params['cltv_expiry_delta'],
|
fee_base_msat=0,
|
||||||
node_features=trampoline_features))
|
fee_proportional_millionths=0,
|
||||||
if trampoline2:
|
cltv_expiry_delta=0,
|
||||||
|
node_features=trampoline_features)
|
||||||
|
]
|
||||||
|
# trampoline hop
|
||||||
route.append(
|
route.append(
|
||||||
TrampolineEdge(
|
TrampolineEdge(
|
||||||
node_id=trampoline2,
|
node_id=trampoline_node_id,
|
||||||
fee_base_msat=params['fee_base_msat'],
|
fee_base_msat=params['fee_base_msat'],
|
||||||
fee_proportional_millionths=params['fee_proportional_millionths'],
|
fee_proportional_millionths=params['fee_proportional_millionths'],
|
||||||
cltv_expiry_delta=params['cltv_expiry_delta'],
|
cltv_expiry_delta=params['cltv_expiry_delta'],
|
||||||
node_features=trampoline_features))
|
node_features=trampoline_features))
|
||||||
# add routing info
|
if trampoline2:
|
||||||
if is_legacy:
|
route.append(
|
||||||
invoice_routing_info = self.encode_routing_info(r_tags)
|
TrampolineEdge(
|
||||||
route[-1].invoice_routing_info = invoice_routing_info
|
node_id=trampoline2,
|
||||||
route[-1].invoice_features = invoice_features
|
fee_base_msat=params['fee_base_msat'],
|
||||||
|
fee_proportional_millionths=params['fee_proportional_millionths'],
|
||||||
|
cltv_expiry_delta=params['cltv_expiry_delta'],
|
||||||
|
node_features=trampoline_features))
|
||||||
|
# add routing info
|
||||||
|
if is_legacy:
|
||||||
|
invoice_routing_info = self.encode_routing_info(r_tags)
|
||||||
|
route[-1].invoice_routing_info = invoice_routing_info
|
||||||
|
route[-1].invoice_features = invoice_features
|
||||||
|
else:
|
||||||
|
if t_tag:
|
||||||
|
pubkey, feebase, feerate, cltv = t_tag
|
||||||
|
if route[-1].node_id != pubkey:
|
||||||
|
route.append(
|
||||||
|
TrampolineEdge(
|
||||||
|
node_id=pubkey,
|
||||||
|
fee_base_msat=feebase,
|
||||||
|
fee_proportional_millionths=feerate,
|
||||||
|
cltv_expiry_delta=cltv,
|
||||||
|
node_features=trampoline_features))
|
||||||
|
# Fake edge (not part of actual route, needed by calc_hops_data)
|
||||||
|
route.append(
|
||||||
|
TrampolineEdge(
|
||||||
|
node_id=invoice_pubkey,
|
||||||
|
fee_base_msat=0,
|
||||||
|
fee_proportional_millionths=0,
|
||||||
|
cltv_expiry_delta=0,
|
||||||
|
node_features=trampoline_features))
|
||||||
|
# check that we can pay amount and fees
|
||||||
|
for edge in route[::-1]:
|
||||||
|
amount_msat += edge.fee_for_edge(amount_msat)
|
||||||
|
if not chan.can_pay(amount_msat, check_frozen=True):
|
||||||
|
continue
|
||||||
|
if not is_route_sane_to_use(route, amount_msat, min_cltv_expiry):
|
||||||
|
continue
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
if t_tag:
|
raise NoPathFound()
|
||||||
pubkey, feebase, feerate, cltv = t_tag
|
self.logger.info(f'created route with trampoline: fee_level={self.trampoline_fee_level}, is legacy: {is_legacy}')
|
||||||
if route[-1].node_id != pubkey:
|
self.logger.info(f'first trampoline: {trampoline_node_id.hex()}')
|
||||||
route.append(
|
self.logger.info(f'second trampoline: {trampoline2.hex() if trampoline2 else None}')
|
||||||
TrampolineEdge(
|
self.logger.info(f'params: {params}')
|
||||||
node_id=pubkey,
|
|
||||||
fee_base_msat=feebase,
|
|
||||||
fee_proportional_millionths=feerate,
|
|
||||||
cltv_expiry_delta=cltv,
|
|
||||||
node_features=trampoline_features))
|
|
||||||
# Fake edge (not part of actual route, needed by calc_hops_data)
|
|
||||||
route.append(
|
|
||||||
TrampolineEdge(
|
|
||||||
node_id=invoice_pubkey,
|
|
||||||
fee_base_msat=0,
|
|
||||||
fee_proportional_millionths=0,
|
|
||||||
cltv_expiry_delta=0,
|
|
||||||
node_features=trampoline_features))
|
|
||||||
return route
|
return route
|
||||||
|
|
||||||
@profiler
|
@profiler
|
||||||
@@ -1422,7 +1429,6 @@ class LNWallet(LNWorker):
|
|||||||
# preference -funds = (low rating=high preference).
|
# preference -funds = (low rating=high preference).
|
||||||
split_configurations = suggest_splits(amount_msat, channels_with_funds)
|
split_configurations = suggest_splits(amount_msat, channels_with_funds)
|
||||||
for s in split_configurations:
|
for s in split_configurations:
|
||||||
self.logger.info(f"trying split configuration: {s[0]} rating: {s[1]}")
|
|
||||||
routes = []
|
routes = []
|
||||||
try:
|
try:
|
||||||
for chanid, part_amount_msat in s[0].items():
|
for chanid, part_amount_msat in s[0].items():
|
||||||
@@ -1441,11 +1447,12 @@ class LNWallet(LNWorker):
|
|||||||
channel,
|
channel,
|
||||||
full_path=None)
|
full_path=None)
|
||||||
routes.append((route, amt))
|
routes.append((route, amt))
|
||||||
|
self.logger.info(f"found acceptable split configuration: {list(s[0].values())} rating: {s[1]}")
|
||||||
break
|
break
|
||||||
except NoPathFound:
|
except NoPathFound:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
raise NoPathFound
|
raise NoPathFound()
|
||||||
return routes
|
return routes
|
||||||
|
|
||||||
def create_route_for_payment(
|
def create_route_for_payment(
|
||||||
@@ -1461,7 +1468,7 @@ class LNWallet(LNWorker):
|
|||||||
channels = [outgoing_channel] if outgoing_channel else list(self.channels.values())
|
channels = [outgoing_channel] if outgoing_channel else list(self.channels.values())
|
||||||
if not self.channel_db:
|
if not self.channel_db:
|
||||||
route = self.create_trampoline_route(
|
route = self.create_trampoline_route(
|
||||||
amount_msat, invoice_pubkey, invoice_features, channels, r_tags, t_tags)
|
amount_msat, min_cltv_expiry, invoice_pubkey, invoice_features, channels, r_tags, t_tags)
|
||||||
return route, amount_msat
|
return route, amount_msat
|
||||||
|
|
||||||
route = None
|
route = None
|
||||||
|
|||||||
Reference in New Issue
Block a user