diff --git a/lndmanage/lib/liquidityhints.py b/lndmanage/lib/liquidityhints.py index a67aecd..d31f344 100644 --- a/lndmanage/lib/liquidityhints.py +++ b/lndmanage/lib/liquidityhints.py @@ -6,7 +6,7 @@ from math import inf, log import logging -from lib.data_types import NodeID, ShortChannelID +from lib.data_types import NodeID, NodePair logger = logging.getLogger(__name__) logger.addHandler(logging.NullHandler()) @@ -163,7 +163,7 @@ class LiquidityHintMgr: # TODO: hints based on node pairs only (shadow channels, non-strict forwarding)? def __init__(self, source_node: str): self.source_node = source_node - self._liquidity_hints: Dict[ShortChannelID, LiquidityHint] = {} + self._liquidity_hints: Dict[NodePair, LiquidityHint] = {} # could_not_route tracks node's failures to route self._could_not_route: Dict[NodeID, int] = defaultdict(int) # could_route tracks node's successes to route @@ -184,22 +184,24 @@ class LiquidityHintMgr: def now(self): return time.time() - def get_hint(self, channel_id: ShortChannelID) -> LiquidityHint: - hint = self._liquidity_hints.get(channel_id) + def _get_hint(self, node_pair: NodePair) -> LiquidityHint: + hint = self._liquidity_hints.get(node_pair) if not hint: hint = LiquidityHint() - self._liquidity_hints[channel_id] = hint + self._liquidity_hints[node_pair] = hint return hint - def update_can_send(self, node_from: NodeID, node_to: NodeID, channel_id: ShortChannelID, amount_msat: int): - logger.debug(f" report: can send {amount_msat // 1000} sat over channel {channel_id}") - hint = self.get_hint(channel_id) + def update_can_send(self, node_from: NodeID, node_to: NodeID, amount_msat: int): + node_pair = NodePair((node_from, node_to)) + logger.debug(f" report: can send {amount_msat // 1000} sat over channel {node_pair}") + hint = self._get_hint(node_pair) hint.update_can_send(node_from < node_to, amount_msat) self._could_route[node_from] += 1 - def update_cannot_send(self, node_from: NodeID, node_to: NodeID, channel_id: ShortChannelID, amount: int): - logger.debug(f" report: cannot send {amount // 1000} sat over channel {channel_id}") - hint = self.get_hint(channel_id) + def update_cannot_send(self, node_from: NodeID, node_to: NodeID, amount: int): + node_pair = NodePair((node_from, node_to)) + logger.debug(f" report: cannot send {amount // 1000} sat over channel {node_pair}") + hint = self._get_hint(node_pair) hint.update_cannot_send(node_from < node_to, amount) self._could_not_route[node_from] += 1 @@ -222,12 +224,14 @@ class LiquidityHintMgr: avg_time = self._elapsed_time[node] / nfwd if nfwd else 0 logger.debug(f" report: update elapsed time {elapsed_time} +=> {self._elapsed_time[node]} (avg: {avg_time}) (node: {node})") - def add_htlc(self, node_from: NodeID, node_to: NodeID, channel_id: ShortChannelID): - hint = self.get_hint(channel_id) + def add_htlc(self, node_from: NodeID, node_to: NodeID): + node_pair = NodePair((node_from, node_to)) + hint = self._get_hint(node_pair) hint.add_htlc(node_from < node_to) - def remove_htlc(self, node_from: NodeID, node_to: NodeID, channel_id: ShortChannelID): - hint = self.get_hint(channel_id) + def remove_htlc(self, node_from: NodeID, node_to: NodeID): + node_pair = NodePair((node_from, node_to)) + hint = self._get_hint(node_pair) hint.remove_htlc(node_from < node_to) def penalty(self, node_from: NodeID, node_to: NodeID, edge: Dict, amount_msat: int, fee_rate_milli_msat: int) -> float: @@ -255,7 +259,7 @@ class LiquidityHintMgr: if self.source_node in [node_from, ]: return 0 # we only evaluate hints here, so use dict get (to not create many hints with self.get_hint) - hint = self._liquidity_hints.get(edge['channel_id']) + hint = self._liquidity_hints.get(edge['node_pair']) if not hint: can_send, cannot_send, num_inflight_htlcs = None, None, 0 else: @@ -305,12 +309,12 @@ class LiquidityHintMgr: self._badness_hints[node_from] *= math.exp(-time_delta / BADNESS_DECAY_SEC) return amount * self._badness_hints[node_from] - def add_to_blacklist(self, channel_id: ShortChannelID): - hint = self.get_hint(channel_id) + def add_to_blacklist(self, node_pair: NodePair): + hint = self._get_hint(node_pair) now = int(time.time()) hint.blacklist_timestamp = now - def get_blacklist(self) -> Set[ShortChannelID]: + def get_blacklist(self) -> Set[NodePair]: now = int(time.time()) return set(k for k, v in self._liquidity_hints.items() if now - v.blacklist_timestamp < BLACKLIST_DURATION) diff --git a/lndmanage/lib/rebalance.py b/lndmanage/lib/rebalance.py index 37d4cf4..239b48e 100644 --- a/lndmanage/lib/rebalance.py +++ b/lndmanage/lib/rebalance.py @@ -151,7 +151,9 @@ class Rebalancer(object): self.node.network.liquidity_hints.update_elapsed_time(source_node, elapsed_time / success_path_length) if failed_hop_index and hop == failed_hop_index: break - self.node.network.liquidity_hints.update_can_send(source_node, target_node, channel['chan_id'], amt_msat) + self.node.network.liquidity_hints.update_can_send( + source_node, target_node, amt_msat, + ) # symmetrically penalize a route about the error source if it failed if failed_hop_index: @@ -206,7 +208,8 @@ class Rebalancer(object): # report that channel could not route the amount to liquidity hints self.node.network.liquidity_hints.update_cannot_send( - failed_source, failed_target, failed_channel_id, amt_msat) + failed_source, failed_target, amt_msat, + ) # report all the previous hops that they could route the amount report_success_up_to_failed_hop(failed_hop) diff --git a/test/test_pathfinding.py b/test/test_pathfinding.py index 0780c41..814f7fe 100644 --- a/test/test_pathfinding.py +++ b/test/test_pathfinding.py @@ -6,6 +6,7 @@ import networkx as nx from test import testing_common +from lndmanage.lib.data_types import NodePair from lndmanage.lib.network import Network from lndmanage.lib.rating import ChannelRater from lndmanage.lib.pathfinding import dijkstra @@ -44,6 +45,7 @@ def new_test_graph(graph: Dict): network.edges[channel] = { 'node1_pub': node, 'node2_pub': to_node, + 'node_pair': NodePair((node, to_node)), 'capacity': channel_definition['capacity'], 'last_update': None, 'channel_id': channel, @@ -61,6 +63,7 @@ def new_test_graph(graph: Dict): channel_id=channel, last_update=None, capacity=channel_definition['capacity'], + node_pair=NodePair((node, to_node)), fees={ node > to_node: channel_definition['policies'][node > to_node], to_node > node: channel_definition['policies'][to_node > node], @@ -103,16 +106,16 @@ class TestGraph(TestCase): self.assertEqual(['A', 'B', 'E'], path) # We report that B cannot send to E - network.liquidity_hints.update_cannot_send('B', 'E', 2, 1_000) + network.liquidity_hints.update_cannot_send('B', 'E', 1_000) path = dijkstra(network.graph, 'A', 'E', weight=weight_function) self.assertEqual(['A', 'D', 'E'], path) # We report that D cannot send to E - network.liquidity_hints.update_cannot_send('D', 'E', 5, 1_000) + network.liquidity_hints.update_cannot_send('D', 'E', 1_000) path = dijkstra(network.graph, 'A', 'E', weight=weight_function) self.assertEqual(['A', 'B', 'C', 'E'], path) # We report that D can send to C - network.liquidity_hints.update_can_send('D', 'C', 4, amt_msat + 1000) + network.liquidity_hints.update_can_send('D', 'C', amt_msat + 1000) path = dijkstra(network.graph, 'A', 'E', weight=weight_function) self.assertEqual(['A', 'D', 'C', 'E'], path)