liquidityhints: switch to pair based keying

This commit is contained in:
bitromortac 2022-05-06 15:38:10 +02:00
parent 660419b00c
commit ef31e8948c
No known key found for this signature in database
GPG key ID: 1965063FC13BEBE2
3 changed files with 34 additions and 24 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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)