Skip to content

Commit

Permalink
Fix rebalance plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
Gálli Zoltán authored and chrisguida committed Mar 12, 2024
1 parent 4a04caf commit 83a80d1
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 27 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Community curated plugins for Core-Lightning.
| [poncho][poncho] | Turns CLN into a [hosted channels][blip12] provider |
| [pruning][pruning] | This plugin manages pruning of bitcoind such that it can always sync |
| [python-teos][python-teos] | The Eye of Satoshi - Lightning Watchtower |
| [rebalance][rebalance] | Keeps your channels balanced |
| [reckless][reckless] | An **experimental** plugin manager (search/install plugins) |
| [sauron][sauron] | A Bitcoin backend relying on [Esplora][esplora]'s API |
| [sitzprobe][sitzprobe] | A Lightning Network payment rehearsal utility |
Expand Down Expand Up @@ -63,7 +64,6 @@ If you like a plugin from that list, feel free to update and fix it, so we can u
| [paytest][paytest] | A plugin to benchmark the performance of the ~pay~ plugin |
| [probe][probe] | Regularly probes the network for stability |
| [prometheus][prometheus] | Lightning node exporter for the prometheus timeseries server |
| [rebalance][rebalance] | Keeps your channels balanced |
| [summary][summary] | Print a nice summary of the node status |

## Installation
Expand Down Expand Up @@ -243,7 +243,7 @@ Python plugins developers must ensure their plugin to work with all Python versi
[python-api]: https://github.com/ElementsProject/lightning/tree/master/contrib/pylightning
[python-api-pypi]: https://pypi.org/project/pylightning/
[python-teos]: https://github.com/talaia-labs/python-teos
[rebalance]: https://github.com/lightningd/plugins/tree/master/archived/rebalance
[rebalance]: https://github.com/lightningd/plugins/tree/master/rebalance
[reckless]: https://github.com/darosior/reckless
[reporter]: https://github.com/LNOpenMetrics/go-lnmetrics.reporter
[sauron]: https://github.com/lightningd/plugins/tree/master/sauron
Expand Down
File renamed without changes.
File renamed without changes.
50 changes: 25 additions & 25 deletions archived/rebalance/rebalance.py → rebalance/rebalance.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,15 +182,15 @@ class NoRouteException(Exception):
pass


def getroute_basic(targetid, fromid, excludes, msatoshi: Millisatoshi):
def getroute_basic(targetid, fromid, excludes, amount_msat: Millisatoshi):
try:
""" This does not make special assumptions and tries all routes
it gets. Uses less CPU and does not filter any routes.
"""
return plugin.rpc.getroute(targetid,
fromid=fromid,
exclude=excludes,
msatoshi=msatoshi,
amount_msat=amount_msat,
maxhops=plugin.maxhops,
riskfactor=10, cltv=9)
except RpcError as e:
Expand All @@ -200,7 +200,7 @@ def getroute_basic(targetid, fromid, excludes, msatoshi: Millisatoshi):
raise e


def getroute_iterative(targetid, fromid, excludes, msatoshi: Millisatoshi):
def getroute_iterative(targetid, fromid, excludes, amount_msat: Millisatoshi):
""" This searches for 'shorter and bigger pipes' first in order
to increase likelyhood of success on short timeout.
Can be useful for manual `rebalance`.
Expand All @@ -209,7 +209,7 @@ def getroute_iterative(targetid, fromid, excludes, msatoshi: Millisatoshi):
return plugin.rpc.getroute(targetid,
fromid=fromid,
exclude=excludes,
msatoshi=msatoshi * plugin.msatfactoridx,
amount_msat=amount_msat * plugin.msatfactoridx,
maxhops=plugin.maxhopidx,
riskfactor=10, cltv=9)
except RpcError as e:
Expand Down Expand Up @@ -333,7 +333,7 @@ def rebalance(plugin, outgoing_scid, incoming_scid, msatoshi: Millisatoshi = Non
r = getroute(targetid=incoming_node_id,
fromid=outgoing_node_id,
excludes=excludes,
msatoshi=msatoshi)
amount_msat=msatoshi)
time_getroute += time.time() - time_start
except NoRouteException:
# no more chance for a successful getroute
Expand Down Expand Up @@ -474,14 +474,14 @@ def check_liquidity_threshold(channels: list, threshold: Millisatoshi):
total = sum(ch["total_msat"] for ch in channels)
required = Millisatoshi(0)
for ch in channels:
required += min(threshold, ch["total_msat"] / 2)
required += min(threshold, Millisatoshi(ch["total_msat"]) / 2)
return required < our and required < total - our


def get_enough_liquidity_threshold(channels: list):
low = Millisatoshi(0)
biggest_channel = max(channels, key=lambda ch: ch["total_msat"])
high = biggest_channel["total_msat"] / 2
high = Millisatoshi(biggest_channel["total_msat"]) / 2
while True:
mid = (low + high) / 2
if high - low < Millisatoshi("1sat"):
Expand All @@ -498,20 +498,20 @@ def get_ideal_ratio(channels: list, enough_liquidity: Millisatoshi):
# small channels should have a 50/50 liquidity ratio to be usable
# and big channels can store the remaining liquidity above the threshold
assert len(channels) > 0
our = sum(ch["to_us_msat"] for ch in channels)
total = sum(ch["total_msat"] for ch in channels)
our = sum(Millisatoshi(ch["to_us_msat"]) for ch in channels)
total = sum(Millisatoshi(ch["total_msat"]) for ch in channels)
chs = list(channels) # get a copy!
while len(chs) > 0:
ratio = int(our) / int(total)
smallest_channel = min(chs, key=lambda ch: ch["total_msat"])
if smallest_channel["total_msat"] * min(ratio, 1 - ratio) > enough_liquidity:
if Millisatoshi(smallest_channel["total_msat"]) * min(ratio, 1 - ratio) > enough_liquidity:
break
min_liquidity = min(smallest_channel["total_msat"] / 2, enough_liquidity)
diff = smallest_channel["total_msat"] * ratio
min_liquidity = min(Millisatoshi(smallest_channel["total_msat"]) / 2, enough_liquidity)
diff = Millisatoshi(smallest_channel["total_msat"]) * ratio
diff = max(diff, min_liquidity)
diff = min(diff, smallest_channel["total_msat"] - min_liquidity)
diff = min(diff, Millisatoshi(smallest_channel["total_msat"]) - min_liquidity)
our -= diff
total -= smallest_channel["total_msat"]
total -= Millisatoshi(smallest_channel["total_msat"])
chs.remove(smallest_channel)
assert 0 <= ratio and ratio <= 1
return ratio
Expand Down Expand Up @@ -552,14 +552,14 @@ def get_chan(scid: str):

def liquidity_info(channel, enough_liquidity: Millisatoshi, ideal_ratio: float):
liquidity = {
"our": channel["to_us_msat"],
"their": channel["total_msat"] - channel["to_us_msat"],
"min": min(enough_liquidity, channel["total_msat"] / 2),
"max": max(a_minus_b(channel["total_msat"], enough_liquidity), channel["total_msat"] / 2),
"our": Millisatoshi(channel["to_us_msat"]),
"their": Millisatoshi(channel["total_msat"] - channel["to_us_msat"]),
"min": min(enough_liquidity, Millisatoshi(channel["total_msat"]) / 2),
"max": max(a_minus_b(Millisatoshi(channel["total_msat"]), enough_liquidity), Millisatoshi(channel["total_msat"]) / 2),
"ideal": {}
}
liquidity["ideal"]["our"] = min(max(channel["total_msat"] * ideal_ratio, liquidity["min"]), liquidity["max"])
liquidity["ideal"]["their"] = min(max(channel["total_msat"] * (1 - ideal_ratio), liquidity["min"]), liquidity["max"])
liquidity["ideal"]["our"] = min(max(Millisatoshi(channel["total_msat"]) * ideal_ratio, liquidity["min"]), liquidity["max"])
liquidity["ideal"]["their"] = min(max(Millisatoshi(channel["total_msat"]) * (1 - ideal_ratio), liquidity["min"]), liquidity["max"])
return liquidity


Expand Down Expand Up @@ -971,7 +971,7 @@ def rebalancereport(plugin: Plugin, include_avg_fees: bool = True):


@plugin.init()
def init(options, configuration, plugin):
def init(options: dict, configuration: dict, plugin: Plugin, **kwargs):
rpchelp = plugin.rpc.help().get('help')
# detect if server cli has moved `listpeers.channels[]` to `listpeerchannels`
# See https://github.com/ElementsProject/lightning/pull/5825
Expand All @@ -983,10 +983,10 @@ def init(options, configuration, plugin):
# do all the stuff that needs to be done just once ...
plugin.getinfo = plugin.rpc.getinfo()
plugin.rpcversion = cln_parse_rpcversion(plugin.getinfo.get('version'))
config = plugin.rpc.listconfigs()
plugin.cltv_final = config.get("cltv-final")
plugin.fee_base = Millisatoshi(config.get("fee-base"))
plugin.fee_ppm = config.get("fee-per-satoshi")
config = plugin.rpc.listconfigs()["configs"]
plugin.cltv_final = config["cltv-final"]["value_int"]
plugin.fee_base = Millisatoshi(config["fee-base"]["value_int"])
plugin.fee_ppm = config["fee-per-satoshi"]["value_int"]
plugin.mutex = threading.Lock()
plugin.maxhops = int(options.get("rebalance-maxhops"))
plugin.msatfactor = float(options.get("rebalance-msatfactor"))
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,25 @@ def test_rebalance_starts(node_factory):
l1 = node_factory.get_node()
# Test dynamically
l1.rpc.plugin_start(plugin_path)
l1.daemon.wait_for_log("Plugin rebalance initialized.*")
l1.rpc.plugin_stop(plugin_path)
l1.rpc.plugin_start(plugin_path)
l1.daemon.wait_for_log("Plugin rebalance initialized.*")
l1.stop()
# Then statically
l1.daemon.opts["plugin"] = plugin_path
l1.start()
# Start at 0 and 're-await' the two inits above. Otherwise this is flaky.
l1.daemon.logsearch_start = 0
l1.daemon.wait_for_logs(["Plugin rebalance initialized.*",
"Plugin rebalance initialized.*",
"Plugin rebalance initialized.*"])


def test_rebalance_manual(node_factory, bitcoind):
l1, l2, l3 = node_factory.line_graph(3, opts=plugin_opt)
l1.daemon.logsearch_start = 0
l1.daemon.wait_for_log("Plugin rebalance initialized.*")
nodes = [l1, l2, l3]

# form a circle so we can do rebalancing
Expand Down Expand Up @@ -85,6 +94,8 @@ def test_rebalance_manual(node_factory, bitcoind):

def test_rebalance_all(node_factory, bitcoind):
l1, l2, l3 = node_factory.line_graph(3, opts=plugin_opt)
l1.daemon.logsearch_start = 0
l1.daemon.wait_for_log("Plugin rebalance initialized.*")
nodes = [l1, l2, l3]

# check we get an error if theres just one channel
Expand Down

0 comments on commit 83a80d1

Please sign in to comment.