Browse Source

lnrouter: filter out unsuitable channels

regtest_lnd
ThomasV 6 years ago
committed by SomberNight
parent
commit
428a768b67
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 0
      electrum/lnaddr.py
  2. 17
      electrum/lnbase.py
  3. 19
      electrum/lnchan.py
  4. 7
      electrum/lnrouter.py
  5. 4
      electrum/lnworker.py

0
electrum/lnaddr.py

17
electrum/lnbase.py

@ -14,6 +14,7 @@ from typing import List
import aiorpcx import aiorpcx
from .crypto import sha256
from . import bitcoin from . import bitcoin
from . import ecc from . import ecc
from .ecc import sig_string_from_r_and_s, get_r_and_s_from_sig_string from .ecc import sig_string_from_r_and_s, get_r_and_s_from_sig_string
@ -921,22 +922,8 @@ class Peer(PrintError):
hops_data += [OnionHopsDataSingle(OnionPerHop(b"\x00"*8, amount_msat.to_bytes(8, "big"), (final_cltv_expiry_without_deltas).to_bytes(4, "big")))] hops_data += [OnionHopsDataSingle(OnionPerHop(b"\x00"*8, amount_msat.to_bytes(8, "big"), (final_cltv_expiry_without_deltas).to_bytes(4, "big")))]
onion = new_onion_packet([x.node_id for x in route], secret_key, hops_data, associated_data) onion = new_onion_packet([x.node_id for x in route], secret_key, hops_data, associated_data)
amount_msat += total_fee amount_msat += total_fee
# FIXME this below will probably break with multiple HTLCs chan.check_can_pay(amount_msat)
msat_local = chan.balance(LOCAL) - amount_msat
msat_remote = chan.balance(REMOTE) + amount_msat
htlc = {'amount_msat':amount_msat, 'payment_hash':payment_hash, 'cltv_expiry':final_cltv_expiry_with_deltas} htlc = {'amount_msat':amount_msat, 'payment_hash':payment_hash, 'cltv_expiry':final_cltv_expiry_with_deltas}
# FIXME if we raise here, this channel will not get blacklisted, and the payment can never succeed,
# as we will just keep retrying this same path. using the current blacklisting is not a solution as
# then no other payment can use this channel either.
# we need finer blacklisting -- e.g. a blacklist for just this "payment session"?
# or blacklist entries could store an msat value and also expire
if len(chan.htlcs(LOCAL, only_pending=True)) + 1 > chan.config[REMOTE].max_accepted_htlcs:
raise PaymentFailure('too many HTLCs already in channel')
if htlcsum(chan.htlcs(LOCAL, only_pending=True)) + amount_msat > chan.config[REMOTE].max_htlc_value_in_flight_msat:
raise PaymentFailure('HTLC value sum would exceed max allowed: {} msat'.format(chan.config[REMOTE].max_htlc_value_in_flight_msat))
if msat_local < 0:
# FIXME what about channel_reserve_satoshis? will the remote fail the channel if we go below? test.
raise PaymentFailure('not enough local balance')
htlc_id = chan.add_htlc(htlc) htlc_id = chan.add_htlc(htlc)
chan.onion_keys[htlc_id] = secret_key chan.onion_keys[htlc_id] = secret_key
self.attempted_route[(chan.channel_id, htlc_id)] = route self.attempted_route[(chan.channel_id, htlc_id)] = route

19
electrum/lnchan.py

@ -143,6 +143,25 @@ class Channel(PrintError):
def get_state(self): def get_state(self):
return self._state return self._state
def check_can_pay(self, amount_msat):
# FIXME what about channel_reserve_satoshis? will the remote fail the channel if we go below? test.
# FIXME what about tx fees
if self.get_state() != 'OPEN':
raise PaymentFailure('Channel not open')
if self.balance(LOCAL) < amount_msat:
raise PaymentFailure('Not enough local balance')
if len(self.htlcs(LOCAL, only_pending=True)) + 1 > self.config[REMOTE].max_accepted_htlcs:
raise PaymentFailure('Too many HTLCs already in channel')
if htlcsum(self.htlcs(LOCAL, only_pending=True)) + amount_msat > self.config[REMOTE].max_htlc_value_in_flight_msat:
raise PaymentFailure('HTLC value sum would exceed max allowed: {} msat'.format(chan.config[REMOTE].max_htlc_value_in_flight_msat))
def can_pay(self, amount_msat):
try:
self.check_can_pay(amount_msat)
except:
return False
return True
def set_funding_txo_spentness(self, is_spent: bool): def set_funding_txo_spentness(self, is_spent: bool):
assert isinstance(is_spent, bool) assert isinstance(is_spent, bool)
self._is_funding_txo_spent = is_spent self._is_funding_txo_spent = is_spent

7
electrum/lnrouter.py

@ -519,7 +519,7 @@ class LNPathFinder(PrintError):
@profiler @profiler
def find_path_for_payment(self, from_node_id: bytes, to_node_id: bytes, def find_path_for_payment(self, from_node_id: bytes, to_node_id: bytes,
amount_msat: int=None) -> Sequence[Tuple[bytes, bytes]]: amount_msat: int=None, my_channels: dict={}) -> Sequence[Tuple[bytes, bytes]]:
"""Return a path between from_node_id and to_node_id. """Return a path between from_node_id and to_node_id.
Returns a list of (node_id, short_channel_id) representing a path. Returns a list of (node_id, short_channel_id) representing a path.
@ -527,6 +527,8 @@ class LNPathFinder(PrintError):
i.e. an element reads as, "to get to node_id, travel through short_channel_id" i.e. an element reads as, "to get to node_id, travel through short_channel_id"
""" """
if amount_msat is not None: assert type(amount_msat) is int if amount_msat is not None: assert type(amount_msat) is int
unable_channels = set(map(lambda x: x.short_channel_id, filter(lambda x: not x.can_pay(amount_msat), my_channels.values())))
# TODO find multiple paths?? # TODO find multiple paths??
# run Dijkstra # run Dijkstra
@ -546,7 +548,8 @@ class LNPathFinder(PrintError):
# so there are duplicates in the queue, that we discard now: # so there are duplicates in the queue, that we discard now:
continue continue
for edge_channel_id in self.channel_db.get_channels_for_node(cur_node): for edge_channel_id in self.channel_db.get_channels_for_node(cur_node):
if edge_channel_id in self.blacklist: continue if edge_channel_id in self.blacklist or edge_channel_id in unable_channels:
continue
channel_info = self.channel_db.get_channel_info(edge_channel_id) channel_info = self.channel_db.get_channel_info(edge_channel_id)
node1, node2 = channel_info.node_id_1, channel_info.node_id_2 node1, node2 = channel_info.node_id_1, channel_info.node_id_2
neighbour = node2 if node1 == cur_node else node1 neighbour = node2 if node1 == cur_node else node1

4
electrum/lnworker.py

@ -280,7 +280,7 @@ class LNWorker(PrintError):
for private_route in r_tags: for private_route in r_tags:
if len(private_route) == 0: continue if len(private_route) == 0: continue
border_node_pubkey = private_route[0][0] border_node_pubkey = private_route[0][0]
path = self.network.path_finder.find_path_for_payment(self.node_keypair.pubkey, border_node_pubkey, amount_msat) path = self.network.path_finder.find_path_for_payment(self.node_keypair.pubkey, border_node_pubkey, amount_msat, self.channels)
if path is None: continue if path is None: continue
route = self.network.path_finder.create_route_from_path(path, self.node_keypair.pubkey) route = self.network.path_finder.create_route_from_path(path, self.node_keypair.pubkey)
# we need to shift the node pubkey by one towards the destination: # we need to shift the node pubkey by one towards the destination:
@ -293,7 +293,7 @@ class LNWorker(PrintError):
break break
# if could not find route using any hint; try without hint now # if could not find route using any hint; try without hint now
if route is None: if route is None:
path = self.network.path_finder.find_path_for_payment(self.node_keypair.pubkey, invoice_pubkey, amount_msat) path = self.network.path_finder.find_path_for_payment(self.node_keypair.pubkey, invoice_pubkey, amount_msat, self.channels)
if path is None: if path is None:
raise PaymentFailure(_("No path found")) raise PaymentFailure(_("No path found"))
route = self.network.path_finder.create_route_from_path(path, self.node_keypair.pubkey) route = self.network.path_finder.create_route_from_path(path, self.node_keypair.pubkey)

Loading…
Cancel
Save