Browse Source

lnworker: don't create invoice with dupl t-hints, & filter when sending

- when creating an invoice, if we had multiple chans with the same trampoline,
  were putting duplicate t-hints into the invoice
- when paying an invoice that had duplicate t-hints, we would sometimes
  construct invalid paths (SRC>T1->T1->DST)
master
SomberNight 2 years ago
parent
commit
a3997f8b58
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 6
      electrum/lnworker.py
  2. 23
      electrum/trampoline.py

6
electrum/lnworker.py

@ -2349,12 +2349,12 @@ class LNWallet(LNWorker):
fee_base_msat,
fee_proportional_millionths,
cltv_expiry_delta)]))
trampoline_hints = []
trampoline_hints = set() # "set", to avoid duplicate t-hints
for r in routing_hints:
node_id, short_channel_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta = r[1][0]
if len(r[1])== 1 and self.is_trampoline_peer(node_id):
trampoline_hints.append(('t', (node_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta)))
return routing_hints, trampoline_hints
trampoline_hints.add(('t', (node_id, fee_base_msat, fee_proportional_millionths, cltv_expiry_delta)))
return routing_hints, list(trampoline_hints)
def delete_payment_info(self, payment_hash_hex: str):
# This method is called when an invoice or request is deleted by the user.

23
electrum/trampoline.py

@ -2,7 +2,7 @@ import os
import bitstring
import random
from typing import Mapping, DefaultDict, Tuple, Optional, Dict, List, Iterable, Sequence
from typing import Mapping, DefaultDict, Tuple, Optional, Dict, List, Iterable, Sequence, Set
from .lnutil import LnFeatures
from .lnonion import calc_hops_data_for_payment, new_onion_packet
@ -97,7 +97,7 @@ def encode_routing_info(r_tags):
return result.tobytes()
def is_legacy_relay(invoice_features, r_tags) -> Tuple[bool, List[bytes]]:
def is_legacy_relay(invoice_features, r_tags) -> Tuple[bool, Set[bytes]]:
"""Returns if we deal with a legacy payment and the list of trampoline pubkeys in the invoice.
"""
invoice_features = LnFeatures(invoice_features)
@ -109,7 +109,7 @@ def is_legacy_relay(invoice_features, r_tags) -> Tuple[bool, List[bytes]]:
# Any trampoline node should be able to figure out a path to the receiver and
# we can use an e2e payment.
if not r_tags:
return False, []
return False, set()
else:
# - We choose one routing hint at random, and
# use end-to-end trampoline if that node is a trampoline-forwarder (TF).
@ -120,10 +120,11 @@ def is_legacy_relay(invoice_features, r_tags) -> Tuple[bool, List[bytes]]:
# recipient only has recv-capacity with T2.
singlehop_r_tags = [x for x in r_tags if len(x) == 1]
invoice_trampolines = [x[0][0] for x in singlehop_r_tags if is_hardcoded_trampoline(x[0][0])]
invoice_trampolines = set(invoice_trampolines)
return False, invoice_trampolines
# if trampoline receiving is not supported or the forwarder is not known as a trampoline,
# we send a legacy payment
return True, []
return True, set()
def trampoline_policy(
@ -161,17 +162,23 @@ def _extend_trampoline_route(
node_features=trampoline_features))
def _choose_second_trampoline(my_trampoline, trampolines, failed_routes: Iterable[Sequence[str]]):
def _choose_second_trampoline(
my_trampoline: bytes,
trampolines: Iterable[bytes],
failed_routes: Iterable[Sequence[str]],
) -> bytes:
trampolines = set(trampolines)
if my_trampoline in trampolines:
trampolines.remove(my_trampoline)
trampolines.discard(my_trampoline)
for r in failed_routes:
if len(r) > 2:
t2 = bytes.fromhex(r[1])
if t2 in trampolines:
trampolines.remove(t2)
trampolines.discard(t2)
if not trampolines:
raise NoPathFound('all routes have failed')
return random.choice(trampolines)
return random.choice(list(trampolines))
def create_trampoline_route(
*,

Loading…
Cancel
Save