diff --git a/README.md b/README.md index 59279a8ff..a0ef9d566 100644 --- a/README.md +++ b/README.md @@ -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 | @@ -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 @@ -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 diff --git a/archived/rebalance/README.md b/rebalance/README.md similarity index 100% rename from archived/rebalance/README.md rename to rebalance/README.md diff --git a/archived/rebalance/clnutils.py b/rebalance/clnutils.py similarity index 100% rename from archived/rebalance/clnutils.py rename to rebalance/clnutils.py diff --git a/archived/rebalance/rebalance.py b/rebalance/rebalance.py similarity index 95% rename from archived/rebalance/rebalance.py rename to rebalance/rebalance.py index 254939e89..b0be90ba1 100755 --- a/archived/rebalance/rebalance.py +++ b/rebalance/rebalance.py @@ -182,7 +182,7 @@ 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. @@ -190,7 +190,7 @@ def getroute_basic(targetid, fromid, excludes, msatoshi: Millisatoshi): 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: @@ -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`. @@ -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: @@ -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 @@ -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"): @@ -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 @@ -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 @@ -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 @@ -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")) diff --git a/archived/rebalance/requirements.txt b/rebalance/requirements.txt similarity index 100% rename from archived/rebalance/requirements.txt rename to rebalance/requirements.txt diff --git a/archived/rebalance/test_rebalance.py b/rebalance/test_rebalance.py similarity index 89% rename from archived/rebalance/test_rebalance.py rename to rebalance/test_rebalance.py index bd016693b..56cefa5d3 100644 --- a/archived/rebalance/test_rebalance.py +++ b/rebalance/test_rebalance.py @@ -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 @@ -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