electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit c9d6d11604b3a90ff1d0e1413da94ff9f96041cd
parent ab9bf07a792bb2637201eed420158d71a9880c22
Author: ThomasV <thomasv@electrum.org>
Date:   Wed, 24 Feb 2021 18:25:49 +0100

create_trampoline_route: check that we can pay the amount and the fees, and that the route is sane

Diffstat:
Melectrum/lnworker.py | 149+++++++++++++++++++++++++++++++++++++++++--------------------------------------
1 file changed, 78 insertions(+), 71 deletions(-)

diff --git a/electrum/lnworker.py b/electrum/lnworker.py @@ -1281,6 +1281,7 @@ class LNWallet(LNWorker): def create_trampoline_route( self, amount_msat:int, + min_cltv_expiry:int, invoice_pubkey:bytes, invoice_features:int, channels: List[Channel], @@ -1304,86 +1305,92 @@ class LNWallet(LNWorker): else: 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 if self.trampoline_fee_level < len(TRAMPOLINE_FEES): params = TRAMPOLINE_FEES[self.trampoline_fee_level] else: raise NoPathFound() - self.logger.info(f'create route with trampoline: fee_level={self.trampoline_fee_level}, is legacy: {is_legacy}') - self.logger.info(f'first trampoline: {trampoline_node_id.hex()}') - self.logger.info(f'second trampoline: {trampoline2.hex() if trampoline2 else None}') - self.logger.info(f'params: {params}') - # node_features is only used to determine is_tlv - trampoline_features = LnFeatures.VAR_ONION_OPT - # hop to trampoline - route = [ - RouteEdge( - node_id=trampoline_node_id, - short_channel_id=trampoline_short_channel_id, - fee_base_msat=0, - fee_proportional_millionths=0, - cltv_expiry_delta=0, - node_features=trampoline_features) - ] - # trampoline hop - route.append( - TrampolineEdge( - node_id=trampoline_node_id, - 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)) - if trampoline2: + # 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 + trampoline_short_channel_id = chan.short_channel_id + trampoline_node_id = chan.node_id + # 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 + # node_features is only used to determine is_tlv + trampoline_features = LnFeatures.VAR_ONION_OPT + # hop to trampoline + route = [ + RouteEdge( + node_id=trampoline_node_id, + short_channel_id=trampoline_short_channel_id, + fee_base_msat=0, + fee_proportional_millionths=0, + cltv_expiry_delta=0, + node_features=trampoline_features) + ] + # trampoline hop route.append( TrampolineEdge( - node_id=trampoline2, + node_id=trampoline_node_id, 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 + if trampoline2: + route.append( + TrampolineEdge( + node_id=trampoline2, + 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: - 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)) + raise NoPathFound() + self.logger.info(f'created route with trampoline: fee_level={self.trampoline_fee_level}, is legacy: {is_legacy}') + self.logger.info(f'first trampoline: {trampoline_node_id.hex()}') + self.logger.info(f'second trampoline: {trampoline2.hex() if trampoline2 else None}') + self.logger.info(f'params: {params}') return route @profiler @@ -1422,7 +1429,6 @@ class LNWallet(LNWorker): # preference -funds = (low rating=high preference). split_configurations = suggest_splits(amount_msat, channels_with_funds) for s in split_configurations: - self.logger.info(f"trying split configuration: {s[0]} rating: {s[1]}") routes = [] try: for chanid, part_amount_msat in s[0].items(): @@ -1441,11 +1447,12 @@ class LNWallet(LNWorker): channel, full_path=None) routes.append((route, amt)) + self.logger.info(f"found acceptable split configuration: {list(s[0].values())} rating: {s[1]}") break except NoPathFound: continue else: - raise NoPathFound + raise NoPathFound() return routes def create_route_for_payment( @@ -1461,7 +1468,7 @@ class LNWallet(LNWorker): channels = [outgoing_channel] if outgoing_channel else list(self.channels.values()) if not self.channel_db: 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 route = None