mirror of
https://github.com/curly60e/pyblock.git
synced 2026-08-15 12:50:38 +02:00
Rebalance-lnd
Added files from Otto's --> https://github.com/C-Otto/rebalance-lnd
This commit is contained in:
parent
0d0c159dbc
commit
fdef46dda0
6 changed files with 13506 additions and 0 deletions
1448
grpc_generated/router_pb2.py
Normal file
1448
grpc_generated/router_pb2.py
Normal file
File diff suppressed because one or more lines are too long
184
grpc_generated/router_pb2_grpc.py
Normal file
184
grpc_generated/router_pb2_grpc.py
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT!
|
||||
import grpc
|
||||
|
||||
from grpc_generated import router_pb2 as router__pb2
|
||||
|
||||
|
||||
class RouterStub(object):
|
||||
# missing associated documentation comment in .proto file
|
||||
pass
|
||||
|
||||
def __init__(self, channel):
|
||||
"""Constructor.
|
||||
|
||||
Args:
|
||||
channel: A grpc.Channel.
|
||||
"""
|
||||
self.SendPayment = channel.unary_stream(
|
||||
'/routerrpc.Router/SendPayment',
|
||||
request_serializer=router__pb2.SendPaymentRequest.SerializeToString,
|
||||
response_deserializer=router__pb2.PaymentStatus.FromString,
|
||||
)
|
||||
self.TrackPayment = channel.unary_stream(
|
||||
'/routerrpc.Router/TrackPayment',
|
||||
request_serializer=router__pb2.TrackPaymentRequest.SerializeToString,
|
||||
response_deserializer=router__pb2.PaymentStatus.FromString,
|
||||
)
|
||||
self.EstimateRouteFee = channel.unary_unary(
|
||||
'/routerrpc.Router/EstimateRouteFee',
|
||||
request_serializer=router__pb2.RouteFeeRequest.SerializeToString,
|
||||
response_deserializer=router__pb2.RouteFeeResponse.FromString,
|
||||
)
|
||||
self.SendToRoute = channel.unary_unary(
|
||||
'/routerrpc.Router/SendToRoute',
|
||||
request_serializer=router__pb2.SendToRouteRequest.SerializeToString,
|
||||
response_deserializer=router__pb2.SendToRouteResponse.FromString,
|
||||
)
|
||||
self.ResetMissionControl = channel.unary_unary(
|
||||
'/routerrpc.Router/ResetMissionControl',
|
||||
request_serializer=router__pb2.ResetMissionControlRequest.SerializeToString,
|
||||
response_deserializer=router__pb2.ResetMissionControlResponse.FromString,
|
||||
)
|
||||
self.QueryMissionControl = channel.unary_unary(
|
||||
'/routerrpc.Router/QueryMissionControl',
|
||||
request_serializer=router__pb2.QueryMissionControlRequest.SerializeToString,
|
||||
response_deserializer=router__pb2.QueryMissionControlResponse.FromString,
|
||||
)
|
||||
self.QueryProbability = channel.unary_unary(
|
||||
'/routerrpc.Router/QueryProbability',
|
||||
request_serializer=router__pb2.QueryProbabilityRequest.SerializeToString,
|
||||
response_deserializer=router__pb2.QueryProbabilityResponse.FromString,
|
||||
)
|
||||
self.BuildRoute = channel.unary_unary(
|
||||
'/routerrpc.Router/BuildRoute',
|
||||
request_serializer=router__pb2.BuildRouteRequest.SerializeToString,
|
||||
response_deserializer=router__pb2.BuildRouteResponse.FromString,
|
||||
)
|
||||
|
||||
|
||||
class RouterServicer(object):
|
||||
# missing associated documentation comment in .proto file
|
||||
pass
|
||||
|
||||
def SendPayment(self, request, context):
|
||||
"""*
|
||||
SendPayment attempts to route a payment described by the passed
|
||||
PaymentRequest to the final destination. The call returns a stream of
|
||||
payment status updates.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def TrackPayment(self, request, context):
|
||||
"""*
|
||||
TrackPayment returns an update stream for the payment identified by the
|
||||
payment hash.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def EstimateRouteFee(self, request, context):
|
||||
"""*
|
||||
EstimateRouteFee allows callers to obtain a lower bound w.r.t how much it
|
||||
may cost to send an HTLC to the target end destination.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def SendToRoute(self, request, context):
|
||||
"""*
|
||||
SendToRoute attempts to make a payment via the specified route. This method
|
||||
differs from SendPayment in that it allows users to specify a full route
|
||||
manually. This can be used for things like rebalancing, and atomic swaps.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def ResetMissionControl(self, request, context):
|
||||
"""*
|
||||
ResetMissionControl clears all mission control state and starts with a clean
|
||||
slate.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def QueryMissionControl(self, request, context):
|
||||
"""*
|
||||
QueryMissionControl exposes the internal mission control state to callers.
|
||||
It is a development feature.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def QueryProbability(self, request, context):
|
||||
"""*
|
||||
QueryProbability returns the current success probability estimate for a
|
||||
given node pair and amount.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
def BuildRoute(self, request, context):
|
||||
"""*
|
||||
BuildRoute builds a fully specified route based on a list of hop public
|
||||
keys. It retrieves the relevant channel policies from the graph in order to
|
||||
calculate the correct fees and time locks.
|
||||
"""
|
||||
context.set_code(grpc.StatusCode.UNIMPLEMENTED)
|
||||
context.set_details('Method not implemented!')
|
||||
raise NotImplementedError('Method not implemented!')
|
||||
|
||||
|
||||
def add_RouterServicer_to_server(servicer, server):
|
||||
rpc_method_handlers = {
|
||||
'SendPayment': grpc.unary_stream_rpc_method_handler(
|
||||
servicer.SendPayment,
|
||||
request_deserializer=router__pb2.SendPaymentRequest.FromString,
|
||||
response_serializer=router__pb2.PaymentStatus.SerializeToString,
|
||||
),
|
||||
'TrackPayment': grpc.unary_stream_rpc_method_handler(
|
||||
servicer.TrackPayment,
|
||||
request_deserializer=router__pb2.TrackPaymentRequest.FromString,
|
||||
response_serializer=router__pb2.PaymentStatus.SerializeToString,
|
||||
),
|
||||
'EstimateRouteFee': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.EstimateRouteFee,
|
||||
request_deserializer=router__pb2.RouteFeeRequest.FromString,
|
||||
response_serializer=router__pb2.RouteFeeResponse.SerializeToString,
|
||||
),
|
||||
'SendToRoute': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.SendToRoute,
|
||||
request_deserializer=router__pb2.SendToRouteRequest.FromString,
|
||||
response_serializer=router__pb2.SendToRouteResponse.SerializeToString,
|
||||
),
|
||||
'ResetMissionControl': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.ResetMissionControl,
|
||||
request_deserializer=router__pb2.ResetMissionControlRequest.FromString,
|
||||
response_serializer=router__pb2.ResetMissionControlResponse.SerializeToString,
|
||||
),
|
||||
'QueryMissionControl': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.QueryMissionControl,
|
||||
request_deserializer=router__pb2.QueryMissionControlRequest.FromString,
|
||||
response_serializer=router__pb2.QueryMissionControlResponse.SerializeToString,
|
||||
),
|
||||
'QueryProbability': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.QueryProbability,
|
||||
request_deserializer=router__pb2.QueryProbabilityRequest.FromString,
|
||||
response_serializer=router__pb2.QueryProbabilityResponse.SerializeToString,
|
||||
),
|
||||
'BuildRoute': grpc.unary_unary_rpc_method_handler(
|
||||
servicer.BuildRoute,
|
||||
request_deserializer=router__pb2.BuildRouteRequest.FromString,
|
||||
response_serializer=router__pb2.BuildRouteResponse.SerializeToString,
|
||||
),
|
||||
}
|
||||
generic_handler = grpc.method_handlers_generic_handler(
|
||||
'routerrpc.Router', rpc_method_handlers)
|
||||
server.add_generic_rpc_handlers((generic_handler,))
|
||||
10187
grpc_generated/rpc_pb2.py
Normal file
10187
grpc_generated/rpc_pb2.py
Normal file
File diff suppressed because one or more lines are too long
1316
grpc_generated/rpc_pb2_grpc.py
Normal file
1316
grpc_generated/rpc_pb2_grpc.py
Normal file
File diff suppressed because it is too large
Load diff
256
rebalance.py
Normal file
256
rebalance.py
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import argparse
|
||||
import math
|
||||
import os
|
||||
import platform
|
||||
import sys
|
||||
|
||||
from lnd import Lnd
|
||||
from logic import Logic
|
||||
|
||||
MAX_CHANNEL_CAPACITY = 16777215
|
||||
MAX_SATOSHIS_PER_TRANSACTION = 4294967
|
||||
|
||||
|
||||
def main():
|
||||
argument_parser = get_argument_parser()
|
||||
arguments = argument_parser.parse_args()
|
||||
lnd = Lnd(arguments.lnddir, arguments.grpc)
|
||||
first_hop_channel_id = vars(arguments)['from']
|
||||
to_channel = arguments.to
|
||||
|
||||
if arguments.ratio < 1 or arguments.ratio > 50:
|
||||
print("--ratio must be between 1 and 50")
|
||||
sys.exit(1)
|
||||
channel_ratio = float(arguments.ratio) / 100
|
||||
|
||||
if arguments.incoming is not None and not arguments.list_candidates:
|
||||
print("--outgoing and --incoming only work in conjunction with --list-candidates")
|
||||
sys.exit(1)
|
||||
|
||||
if arguments.list_candidates:
|
||||
incoming = arguments.incoming is None or arguments.incoming
|
||||
if incoming:
|
||||
list_incoming_candidates(lnd, channel_ratio)
|
||||
else:
|
||||
list_outgoing_candidates(lnd, channel_ratio)
|
||||
sys.exit(0)
|
||||
|
||||
if to_channel is None and first_hop_channel_id is None:
|
||||
argument_parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
percentage = arguments.percentage
|
||||
if percentage:
|
||||
if percentage < 1 or percentage > 100:
|
||||
print("--percentage must be between 1 and 100")
|
||||
argument_parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
# the 'to' argument might be an index, or a channel ID
|
||||
if to_channel and to_channel < 10000:
|
||||
# here we are in the "channel index" case
|
||||
index = int(to_channel) - 1
|
||||
candidates = get_incoming_rebalance_candidates(lnd, channel_ratio)
|
||||
candidate = candidates[index]
|
||||
last_hop_channel = candidate
|
||||
else:
|
||||
# else the channel argument should be the channel ID
|
||||
last_hop_channel = get_channel_for_channel_id(lnd, to_channel)
|
||||
|
||||
first_hop_channel = get_channel_for_channel_id(lnd, first_hop_channel_id)
|
||||
|
||||
amount = get_amount(arguments, first_hop_channel, last_hop_channel)
|
||||
|
||||
if amount == 0:
|
||||
print("Amount is 0, nothing to do")
|
||||
sys.exit(0)
|
||||
|
||||
max_fee_factor = arguments.max_fee_factor
|
||||
excluded = arguments.exclude
|
||||
return Logic(lnd, first_hop_channel, last_hop_channel, amount, channel_ratio, excluded,
|
||||
max_fee_factor).rebalance()
|
||||
|
||||
|
||||
def get_amount(arguments, first_hop_channel, last_hop_channel):
|
||||
if last_hop_channel:
|
||||
amount = get_rebalance_amount(last_hop_channel)
|
||||
else:
|
||||
amount = get_rebalance_amount(first_hop_channel)
|
||||
|
||||
if arguments.percentage:
|
||||
amount = int(round(amount * arguments.percentage / 100))
|
||||
|
||||
if last_hop_channel and first_hop_channel:
|
||||
rebalance_amount_from_channel = get_rebalance_amount(first_hop_channel)
|
||||
amount = min(amount, rebalance_amount_from_channel)
|
||||
|
||||
if arguments.amount:
|
||||
amount = min(amount, int(arguments.amount))
|
||||
|
||||
amount = min(amount, MAX_SATOSHIS_PER_TRANSACTION)
|
||||
|
||||
return amount
|
||||
|
||||
|
||||
def get_channel_for_channel_id(lnd, channel_id):
|
||||
for channel in lnd.get_channels():
|
||||
if channel.chan_id == channel_id:
|
||||
return channel
|
||||
return None
|
||||
|
||||
|
||||
def get_argument_parser():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--lnddir",
|
||||
default="~/.lnd",
|
||||
dest="lnddir",
|
||||
help="(default ~/.lnd) lnd directory")
|
||||
parser.add_argument("--grpc",
|
||||
default="localhost:10009",
|
||||
dest="grpc",
|
||||
help="(default localhost:10009) lnd gRPC endpoint")
|
||||
parser.add_argument("-r", "--ratio",
|
||||
type=int,
|
||||
default=50,
|
||||
help="(default: 50) ratio for channel imbalance between 1 and 50, "
|
||||
"eg. 45 to only show channels (-l) with less than 45%% of the "
|
||||
"funds on the local (-i) or remote (-o) side")
|
||||
list_group = parser.add_argument_group("list candidates", "Show the unbalanced channels.")
|
||||
list_group.add_argument("-l", "--list-candidates", action="store_true",
|
||||
help="list candidate channels for rebalance")
|
||||
|
||||
direction_group = list_group.add_mutually_exclusive_group()
|
||||
direction_group.add_argument("-o", "--outgoing",
|
||||
action="store_const",
|
||||
const=False,
|
||||
dest="incoming",
|
||||
help="lists channels with less than x%% of the funds on the remote side (see --ratio)")
|
||||
direction_group.add_argument("-i", "--incoming",
|
||||
action="store_const",
|
||||
const=True,
|
||||
dest="incoming",
|
||||
help="(default) lists channels with less than x%% of the funds on the local side "
|
||||
"(see --ratio)")
|
||||
|
||||
rebalance_group = parser.add_argument_group("rebalance",
|
||||
"Rebalance a channel. You need to specify at least"
|
||||
" the 'from' channel (-f) or the 'to' channel (-t).")
|
||||
rebalance_group.add_argument("-f", "--from",
|
||||
metavar="CHANNEL",
|
||||
type=int,
|
||||
help="channel ID of the outgoing channel "
|
||||
"(funds will be taken from this channel)")
|
||||
rebalance_group.add_argument("-t", "--to",
|
||||
metavar="CHANNEL",
|
||||
type=int,
|
||||
help="channel ID of the incoming channel "
|
||||
"(funds will be sent to this channel). "
|
||||
"You may also use the index as shown in the incoming candidate list (-l -i).")
|
||||
amount_group = rebalance_group.add_mutually_exclusive_group()
|
||||
amount_group.add_argument("-a", "--amount",
|
||||
type=int,
|
||||
help="Amount of the rebalance, in satoshis. If not specified, "
|
||||
"the amount computed for a perfect rebalance will be used"
|
||||
" (up to the maximum of 4,294,967 satoshis)")
|
||||
amount_group.add_argument("-p", "--percentage",
|
||||
type=int,
|
||||
help="Set the amount to send to a percentage of the amount required to rebalance. "
|
||||
"As an example, if this is set to 50, the amount will half of the default. "
|
||||
"See --amount.")
|
||||
rebalance_group.add_argument("-e", "--exclude",
|
||||
type=int,
|
||||
action="append",
|
||||
help="Exclude the given channel ID as the outgoing channel (no funds will be taken "
|
||||
"out of excluded channels)")
|
||||
rebalance_group.add_argument("--max-fee-factor",
|
||||
type=float,
|
||||
default=10,
|
||||
help="(default: 10) Reject routes that cost more than x times the lnd default "
|
||||
"(base: 1 sat, rate: 1 millionth sat) per hop on average")
|
||||
return parser
|
||||
|
||||
|
||||
def list_incoming_candidates(lnd, channel_ratio):
|
||||
candidates = get_incoming_rebalance_candidates(lnd, channel_ratio)
|
||||
list_candidates(candidates)
|
||||
|
||||
|
||||
def list_outgoing_candidates(lnd, channel_ratio):
|
||||
candidates = get_outgoing_rebalance_candidates(lnd, channel_ratio)
|
||||
list_candidates(candidates)
|
||||
|
||||
|
||||
def list_candidates(candidates):
|
||||
index = 0
|
||||
for candidate in candidates:
|
||||
index += 1
|
||||
rebalance_amount_int = get_rebalance_amount(candidate)
|
||||
rebalance_amount = "{:,}".format(rebalance_amount_int)
|
||||
if rebalance_amount_int > MAX_SATOSHIS_PER_TRANSACTION:
|
||||
rebalance_amount += " (max per transaction: {:,})".format(MAX_SATOSHIS_PER_TRANSACTION)
|
||||
|
||||
print("(%2d) Channel ID: " % index + str(candidate.chan_id))
|
||||
print("Pubkey: " + candidate.remote_pubkey)
|
||||
print("Local ratio: {:.3f}".format(get_local_ratio(candidate)))
|
||||
print("Capacity: {:,}".format(candidate.capacity))
|
||||
print("Remote balance: {:,}".format(candidate.remote_balance))
|
||||
print("Local balance: {:,}".format(candidate.local_balance))
|
||||
print("Amount for 50-50: " + rebalance_amount)
|
||||
print(get_capacity_and_ratio_bar(candidate))
|
||||
print("")
|
||||
|
||||
|
||||
def get_rebalance_amount(channel):
|
||||
return abs(int(math.ceil(float(get_remote_surplus(channel)) / 2)))
|
||||
|
||||
|
||||
def get_incoming_rebalance_candidates(lnd, channel_ratio):
|
||||
low_local = list(filter(lambda c: get_local_ratio(c) < channel_ratio, lnd.get_channels()))
|
||||
low_local = list(filter(lambda c: get_rebalance_amount(c) > 0, low_local))
|
||||
return sorted(low_local, key=get_remote_surplus, reverse=False)
|
||||
|
||||
|
||||
def get_outgoing_rebalance_candidates(lnd, channel_ratio):
|
||||
high_local = list(filter(lambda c: get_local_ratio(c) > 1 - channel_ratio, lnd.get_channels()))
|
||||
high_local = list(filter(lambda c: get_rebalance_amount(c) > 0, high_local))
|
||||
return sorted(high_local, key=get_remote_surplus, reverse=True)
|
||||
|
||||
|
||||
def get_local_ratio(channel):
|
||||
remote = channel.remote_balance
|
||||
local = channel.local_balance
|
||||
return float(local) / (remote + local)
|
||||
|
||||
|
||||
def get_remote_surplus(channel):
|
||||
return channel.remote_balance - channel.local_balance
|
||||
|
||||
|
||||
def get_capacity_and_ratio_bar(candidate):
|
||||
columns = get_columns()
|
||||
columns_scaled_to_capacity = int(round(columns * float(candidate.capacity) / MAX_CHANNEL_CAPACITY))
|
||||
|
||||
bar_width = columns_scaled_to_capacity - 2
|
||||
result = "|"
|
||||
ratio = get_local_ratio(candidate)
|
||||
length = int(round(ratio * bar_width))
|
||||
for x in range(0, length):
|
||||
result += "="
|
||||
for x in range(length, bar_width):
|
||||
result += " "
|
||||
return result + "|"
|
||||
|
||||
|
||||
def get_columns():
|
||||
if platform.system() == 'Linux' and sys.__stdin__.isatty():
|
||||
return int(os.popen('stty size', 'r').read().split()[1])
|
||||
else:
|
||||
return 80
|
||||
|
||||
|
||||
success = main()
|
||||
if success:
|
||||
sys.exit(0)
|
||||
sys.exit(1)
|
||||
115
routes.py
Normal file
115
routes.py
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
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))
|
||||
Loading…
Add table
Add a link
Reference in a new issue