Skip to content

Commit f95f5ec

Browse files
authored
Merge pull request #10646 from f321x/onion_message_improvements_bolt12
onion_message: sending through trampoline, improvements
2 parents 1f4c0fc + b3af267 commit f95f5ec

4 files changed

Lines changed: 203 additions & 131 deletions

File tree

electrum/lnrouter.py

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,7 @@ def _edge_cost(
452452
end_node: bytes,
453453
payment_amt_msat: int,
454454
ignore_costs=False,
455+
ignore_amount_constraints: bool = False,
455456
is_mine=False,
456457
my_channels: Dict[ShortChannelID, 'Channel'] = None,
457458
private_route_edges: Dict[ShortChannelID, RouteEdge] = None,
@@ -481,14 +482,15 @@ def _edge_cost(
481482
return float('inf'), 0
482483
if channel_policy.is_disabled():
483484
return float('inf'), 0
484-
if payment_amt_msat < channel_policy.htlc_minimum_msat:
485-
return float('inf'), 0 # payment amount too little
486-
if channel_info.capacity_sat is not None and \
487-
payment_amt_msat // 1000 > channel_info.capacity_sat:
488-
return float('inf'), 0 # payment amount too large
489-
if channel_policy.htlc_maximum_msat is not None and \
490-
payment_amt_msat > channel_policy.htlc_maximum_msat:
491-
return float('inf'), 0 # payment amount too large
485+
if not ignore_amount_constraints:
486+
if payment_amt_msat < channel_policy.htlc_minimum_msat:
487+
return float('inf'), 0 # payment amount too little
488+
if channel_info.capacity_sat is not None and \
489+
payment_amt_msat // 1000 > channel_info.capacity_sat:
490+
return float('inf'), 0 # payment amount too large
491+
if channel_policy.htlc_maximum_msat is not None and \
492+
payment_amt_msat > channel_policy.htlc_maximum_msat:
493+
return float('inf'), 0 # payment amount too large
492494
route_edge = private_route_edges.get(short_channel_id, None)
493495
if route_edge is None:
494496
node_info = self.channel_db.get_node_info_for_node_id(node_id=end_node)
@@ -513,7 +515,7 @@ def _edge_cost(
513515
# - The larger the payment amount, and the longer the CLTV,
514516
# the more irritating it is if the HTLC gets stuck.
515517
# - Paying lower fees is better. :)
516-
if ignore_costs:
518+
if ignore_costs or ignore_amount_constraints:
517519
return DEFAULT_PENALTY_BASE_MSAT, 0
518520
fee_msat = route_edge.fee_for_edge(payment_amt_msat)
519521
cltv_cost = route_edge.cltv_delta * payment_amt_msat * 15 / 1_000_000_000
@@ -528,10 +530,10 @@ def get_shortest_path_hops(
528530
*,
529531
nodeA: bytes, # nodeA is expected to be our node id if channels are passed in my_sending_channels
530532
nodeB: bytes,
531-
invoice_amount_msat: int,
533+
invoice_amount_msat: Optional[int],
532534
my_sending_channels: Dict[ShortChannelID, 'Channel'] = None,
533535
private_route_edges: Dict[ShortChannelID, RouteEdge] = None,
534-
node_filter: Optional[Callable[[bytes, NodeInfo], bool]] = None
536+
node_filter: Optional[Callable[[bytes, Optional[NodeInfo]], bool]] = None,
535537
) -> Dict[bytes, PathEdge]:
536538
# note: we don't lock self.channel_db, so while the path finding runs,
537539
# the underlying graph could potentially change... (not good but maybe ~OK?)
@@ -545,11 +547,12 @@ def get_shortest_path_hops(
545547
# run Dijkstra
546548
# The search is run in the REVERSE direction, from nodeB to nodeA,
547549
# to properly calculate compound routing fees.
550+
ignore_amount_constraints = invoice_amount_msat is None # e.g. onion messages
548551
distance_from_start = defaultdict(lambda: float('inf'))
549552
distance_from_start[nodeB] = 0
550553
previous_hops = {} # type: Dict[bytes, PathEdge]
551554
nodes_to_explore = queue.PriorityQueue()
552-
nodes_to_explore.put((0, invoice_amount_msat, nodeB)) # order of fields (in tuple) matters!
555+
nodes_to_explore.put((0, invoice_amount_msat or 0, nodeB)) # order of fields (in tuple) matters!
553556
now = int(time.time())
554557

555558
# main loop of search
@@ -592,14 +595,16 @@ def get_shortest_path_hops(
592595
if edge_startnode == nodeA and my_sending_channels: # payment outgoing, on our channel
593596
if edge_channel_id not in my_sending_channels:
594597
continue
595-
if not my_sending_channels[edge_channel_id].can_pay(amount_msat, check_frozen=True):
598+
if not ignore_amount_constraints \
599+
and not my_sending_channels[edge_channel_id].can_pay(amount_msat, check_frozen=True):
596600
continue
597601
edge_cost, fee_for_edge_msat = self._edge_cost(
598602
short_channel_id=edge_channel_id,
599603
start_node=edge_startnode,
600604
end_node=edge_endnode,
601605
payment_amt_msat=amount_msat,
602606
ignore_costs=(edge_startnode == nodeA),
607+
ignore_amount_constraints=ignore_amount_constraints,
603608
is_mine=is_mine,
604609
my_channels=my_sending_channels,
605610
private_route_edges=private_route_edges,
@@ -626,15 +631,15 @@ def find_path_for_payment(
626631
*,
627632
nodeA: bytes,
628633
nodeB: bytes,
629-
invoice_amount_msat: int,
634+
invoice_amount_msat: Optional[int],
630635
my_sending_channels: Dict[ShortChannelID, 'Channel'] = None,
631636
private_route_edges: Dict[ShortChannelID, RouteEdge] = None,
632-
node_filter: Optional[Callable[[bytes, NodeInfo], bool]] = None
637+
node_filter: Optional[Callable[[bytes, Optional[NodeInfo]], bool]] = None
633638
) -> Optional[LNPaymentPath]:
634639
"""Return a path from nodeA to nodeB."""
635640
assert type(nodeA) is bytes
636641
assert type(nodeB) is bytes
637-
assert type(invoice_amount_msat) is int
642+
assert type(invoice_amount_msat) is int or invoice_amount_msat is None
638643
if my_sending_channels is None:
639644
my_sending_channels = {}
640645

@@ -659,6 +664,28 @@ def find_path_for_payment(
659664
edge_startnode = edge.node_id
660665
return path
661666

667+
def find_path_for_onion_message(
668+
self,
669+
*,
670+
nodeA: bytes,
671+
nodeB: bytes,
672+
my_sending_channels: Dict[ShortChannelID, 'Channel'] = None,
673+
private_route_edges: Dict[ShortChannelID, RouteEdge] = None,
674+
) -> Optional[LNPaymentPath]:
675+
from .onion_message import is_onion_message_node
676+
def _node_filter(edge_startnode, node_info):
677+
if edge_startnode == nodeA:
678+
return True # assume the sending node does support onion messages
679+
return is_onion_message_node(edge_startnode, node_info)
680+
return self.find_path_for_payment(
681+
nodeA=nodeA,
682+
nodeB=nodeB,
683+
my_sending_channels=my_sending_channels,
684+
private_route_edges=private_route_edges,
685+
node_filter=_node_filter,
686+
invoice_amount_msat=None,
687+
)
688+
662689
def create_route_from_path(
663690
self,
664691
path: Optional[LNPaymentPath],

0 commit comments

Comments
 (0)