pyblock/pybitblock/routes.py
2021-12-03 15:59:28 -06:00

115 lines
3.7 KiB
Python

import base64
import sys
MAX_ROUTES_TO_REQUEST = 100
def debug(message):
sys.stderr.write(message + "\n")
def debugnobreak(message):
sys.stderr.write(message)
class Routes:
def __init__(self, lnd, payment_request, first_hop_channel, last_hop_channel):
self.lnd = lnd
self.payment_request = payment_request
self.first_hop_channel = first_hop_channel
self.last_hop_channel = last_hop_channel
self.all_routes = []
self.returned_routes = []
self.ignored_edges = []
self.ignored_nodes = []
self.num_requested_routes = 0
def has_next(self):
self.update_routes()
return self.returned_routes < self.all_routes
def get_next(self):
self.update_routes()
for route in self.all_routes:
if route not in self.returned_routes:
self.returned_routes.append(route)
return route
return None
def update_routes(self):
while True:
if self.returned_routes < self.all_routes:
return
if self.num_requested_routes >= MAX_ROUTES_TO_REQUEST:
return
self.request_route()
def request_route(self):
amount = self.get_amount()
if self.last_hop_channel:
last_hop_pubkey = self.last_hop_channel.remote_pubkey
else:
last_hop_pubkey = None
if self.first_hop_channel:
first_hop_channel_id = self.first_hop_channel.chan_id
else:
first_hop_channel_id = None
routes = self.lnd.get_route(last_hop_pubkey, amount, self.ignored_edges,
self.ignored_nodes, first_hop_channel_id)
if routes is None:
self.num_requested_routes = MAX_ROUTES_TO_REQUEST
else:
self.num_requested_routes += 1
for route in routes:
self.add_route(route)
def add_route(self, route):
if route is None:
return
if route not in self.all_routes:
self.all_routes.append(route)
@staticmethod
def print_route(route):
route_str = " -> ".join(str(h.chan_id) for h in route.hops)
return route_str
def get_amount(self):
return self.payment_request.num_satoshis
def ignore_first_hop(self, channel, show_message=True):
own_key = self.lnd.get_own_pubkey()
other_key = channel.remote_pubkey
self.ignore_edge_from_to(channel.chan_id, own_key, other_key, show_message)
def ignore_edge_on_route(self, failure_source_pubkey, route):
ignore_next = False
for hop in route.hops:
if ignore_next:
self.ignore_edge_from_to(hop.chan_id, failure_source_pubkey, hop.pub_key)
return
if hop.pub_key == failure_source_pubkey:
ignore_next = True
def ignore_node_with_highest_fee(self, route):
max_fee_msat = 0
max_fee_hop = None
for hop in route.hops:
if hop.fee_msat > max_fee_msat:
max_fee_msat = hop.fee_msat
max_fee_hop = hop
pub_key = max_fee_hop.pub_key
debugnobreak("High fees (%s msat), " % max_fee_msat)
self.ignore_node(pub_key)
def ignore_edge_from_to(self, chan_id, from_pubkey, to_pubkey, show_message=True):
if show_message:
debug("ignoring channel %s (from %s to %s)" % (chan_id, from_pubkey, to_pubkey))
direction_reverse = from_pubkey > to_pubkey
edge = {"channel_id": chan_id, "direction_reverse": direction_reverse}
self.ignored_edges.append(edge)
def ignore_node(self, pub_key):
debug("ignoring node %s" % pub_key)
self.ignored_nodes.append(base64.b16decode(pub_key, True))