Ejemplo n.º 1
0
def rebalanceall(plugin: Plugin, min_amount: Millisatoshi = Millisatoshi("50000sat"), feeratio: float = 0.5):
    """Rebalance all unbalanced channels if possible for a very low fee.
    Default minimum rebalancable amount is 50000sat. Default feeratio = 0.5, half of our node's default fee.
    To be economical, it tries to fix the liquidity cheaper than it can be ruined by transaction forwards.
    It may run for a long time (hours) in the background, but can be stopped with the rebalancestop method.
    """
    # some early checks before we start the async thread
    if plugin.mutex.locked():
        return {"message": "Rebalance is already running, this may take a while. To stop it use the cli method 'rebalancestop'."}
    channels = get_open_channels(plugin)
    if len(channels) <= 1:
        return {"message": "Error: Not enough open channels to rebalance anything"}
    our = sum(ch["to_us_msat"] for ch in channels)
    total = sum(ch["total_msat"] for ch in channels)
    min_amount = Millisatoshi(min_amount)
    if total - our < min_amount or our < min_amount:
        return {"message": "Error: Not enough liquidity to rebalance anything"}

    # param parsing ensure correct type
    plugin.feeratio = float(feeratio)
    plugin.min_amount = min_amount

    # run the job
    t = Thread(target=rebalanceall_thread, args=(plugin, ))
    t.start()
    return {"message": f"Rebalance started with min rebalancable amount: {plugin.min_amount}, feeratio: {plugin.feeratio}"}
Ejemplo n.º 2
0
def feeadjust(plugin: Plugin, scid: str = None):
    """Adjust fees for all channels (default) or just a given `scid`.

    This method is automatically called in plugin init, or can be called manually after a successful payment.
    Otherwise, the plugin keeps the fees up-to-date.
    """
    plugin.mutex.acquire(blocking=True)
    plugin.peers = plugin.rpc.listpeers()["peers"]
    if plugin.fee_strategy == get_fees_median and not plugin.listchannels_by_dst:
        plugin.channels = plugin.rpc.listchannels()['channels']
    channels_adjusted = 0
    for peer in plugin.peers:
        for chan in peer["channels"]:
            if chan["state"] == "CHANNELD_NORMAL":
                _scid = chan["short_channel_id"]
                if scid != None and scid != _scid:
                    continue
                plugin.adj_balances[_scid] = {
                    "our": int(chan["to_us_msat"]),
                    "total": int(chan["total_msat"])
                }
                channels_adjusted += maybe_adjust_fees(plugin, [_scid])
    msg = f"{channels_adjusted} channel(s) adjusted"
    plugin.log(msg)
    plugin.mutex.release()
    return msg
Ejemplo n.º 3
0
def feeadjust_would_be_nice(plugin: Plugin):
    commands = [c for c in plugin.rpc.help().get("help") if c["command"].split()[0] == "feeadjust"]
    if len(commands) == 1:
        msg = plugin.rpc.feeadjust()
        plugin.log(f"Feeadjust succeeded: {msg}")
    else:
        plugin.log("The feeadjuster plugin would be useful here")
Ejemplo n.º 4
0
def on_init(options: Mapping[str, str], plugin: Plugin, **kwargs):
    # Reach into the DB and
    configs = plugin.rpc.listconfigs()
    plugin.db_path = configs['wallet']
    destination = options['backup-destination']

    # Ensure that we don't inadventently switch the destination
    if not os.path.exists("backup.lock"):
        print("Files in the current directory {}".format(", ".join(
            os.listdir("."))))
        return abort(
            "Could not find backup.lock in the lightning-dir, have you initialized using the backup-cli utility?"
        )

    d = json.load(open("backup.lock", 'r'))
    if destination is None or destination == 'null':
        destination = d['backend_url']
    elif destination != d['backend_url']:
        abort("The destination specified as option does not match the one "
              "specified in backup.lock. Please check your settings")

    if not plugin.db_path.startswith('sqlite3'):
        abort("The backup plugin only works with the sqlite3 database.")

    plugin.backend = get_backend(destination, require_init=True)

    for c in plugin.early_writes:
        apply_write(plugin, c)
Ejemplo n.º 5
0
def shutdown(plugin: Plugin):
    plugin.stop_incoming_htlcs = True
    num_inflight_htlcs = 1
    while num_inflight_htlcs > 0:
        num_inflight_htlcs = count_inflight_htlcs()
        if num_inflight_htlcs > 0:
            plugin.log(f"Waiting for {num_inflight_htlcs} HTLCs to resolve...")
            time.sleep(3)
    plugin.rpc.stop()
Ejemplo n.º 6
0
def rebalancestop(plugin: Plugin):
    """It stops the ongoing rebalanceall.
    """
    if not plugin.mutex.locked():
        return {"message": "No rebalance is running, nothing to stop"}
    plugin.rebalance_stop = True
    plugin.mutex.acquire(blocking=True)
    plugin.rebalance_stop = False
    plugin.mutex.release()
    return {"message": "Rebalance stopped"}
Ejemplo n.º 7
0
def setchannelfee(plugin: Plugin, scid: str, base: int, ppm: int):
    fees = get_chan_fees(plugin, scid)
    if fees is None or base == fees['base'] and ppm == fees['ppm']:
        return False
    try:
        plugin.rpc.setchannelfee(scid, base, ppm)
        return True
    except RpcError as e:
        plugin.log(f"Could not adjust fees for channel {scid}: '{e}'", level="error")
    return False
Ejemplo n.º 8
0
def test_argument_coercion():
    p = Plugin(autopatch=False)

    def test1(msat: Millisatoshi):
        assert isinstance(msat, Millisatoshi)

    ba = p._bind_kwargs(test1, {"msat": "100msat"}, None)
    test1(*ba.args)

    ba = p._bind_pos(test1, ["100msat"], None)
    test1(*ba.args, **ba.kwargs)
Ejemplo n.º 9
0
def maybe_rebalance_pairs(plugin: Plugin, ch1, ch2, failed_pairs: list):
    scid1 = ch1["short_channel_id"]
    scid2 = ch2["short_channel_id"]
    result = {"success": False, "fee_spent": Millisatoshi(0)}
    if scid1 + ":" + scid2 in failed_pairs:
        return result
    i = 0
    while not plugin.rebalance_stop:
        liquidity1 = liquidity_info(ch1, plugin.enough_liquidity,
                                    plugin.ideal_ratio)
        liquidity2 = liquidity_info(ch2, plugin.enough_liquidity,
                                    plugin.ideal_ratio)
        amount1 = min(must_send(liquidity1), could_receive(liquidity2))
        amount2 = min(should_send(liquidity1), should_receive(liquidity2))
        amount3 = min(could_send(liquidity1), must_receive(liquidity2))
        amount = max(amount1, amount2, amount3)
        if amount < plugin.min_amount:
            return result
        amount = min(amount, get_max_amount(i, plugin))
        maxfee = get_max_fee(plugin, amount)
        plugin.log(
            f"Try to rebalance: {scid1} -> {scid2}; amount={amount}; maxfee={maxfee}"
        )
        start_ts = time.time()
        try:
            res = rebalance(plugin,
                            outgoing_scid=scid1,
                            incoming_scid=scid2,
                            msatoshi=amount,
                            maxfeepercent=0,
                            retry_for=1200,
                            exemptfee=maxfee)
        except Exception:
            failed_pairs.append(scid1 + ":" + scid2)
            # rebalance failed, let's try with a smaller amount
            while (get_max_amount(i, plugin) >= amount and
                   get_max_amount(i, plugin) != get_max_amount(i + 1, plugin)):
                i += 1
            if amount > get_max_amount(i, plugin):
                continue
            return result
        elapsed_time = timedelta(seconds=time.time() - start_ts)
        res["elapsed_time"] = str(elapsed_time)[:-3]
        plugin.log(f"Rebalance succeeded: {res}")
        result["success"] = True
        result["fee_spent"] += res["fee"]
        # refresh channels
        time.sleep(10)
        ch1 = get_chan(plugin, scid1)
        assert ch1 is not None
        ch2 = get_chan(plugin, scid2)
        assert ch2 is not None
    return result
Ejemplo n.º 10
0
def feeadjuster_toggle(plugin: Plugin, value: bool = None):
    """Activates/Deactivates automatic fee updates for forward events.

    The status will be set to value.
    """
    msg = {"forward_event_subscription": {"previous": plugin.forward_event_subscription}}
    if value is None:
        plugin.forward_event_subscription = not plugin.forward_event_subscription
    else:
        plugin.forward_event_subscription = bool(value)
    msg["forward_event_subscription"]["current"] = plugin.forward_event_subscription
    return msg
Ejemplo n.º 11
0
def maybe_setchannelfee(plugin: Plugin, scid: str, base: int, ppm: int):
    fees = get_chan_fees(plugin, scid)
    if fees is None or base == fees["base_fee_millisatoshi"] and ppm == fees[
            "fee_per_millionth"]:
        return False
    try:
        plugin.rpc.setchannelfee(scid, base, ppm)
        return True
    except RpcError as e:
        plugin.log(f"Could not adjust fees for channel {scid}: '{e}'",
                   level="error")
    return False
Ejemplo n.º 12
0
def init(plugin: Plugin, options: Mapping[str, str], **_kwargs):
    plugin.boltz_api = options["boltz-api"]
    plugin.boltz_node = options["boltz-node"]

    plugin.data_location = path.join(plugin.rpc.listconfigs()["lightning-dir"], "channel-creation.json")

    print("Channel Creation data location: {}".format(plugin.data_location))
    read_channel_creation(plugin)

    print("Started channel-creation plugin: {}".format({
        "boltz_api": plugin.boltz_api,
        "boltz_node": plugin.boltz_node,
    }))
Ejemplo n.º 13
0
def rebalanceall(plugin: Plugin, min_amount: Millisatoshi = Millisatoshi("50000sat"), feeratio: float = 0.5):
    """Rebalance all unbalanced channels if possible for a very low fee.
    Default minimum rebalancable amount is 50000sat. Default feeratio = 0.5, half of our node's default fee.
    To be economical, it tries to fix the liquidity cheaper than it can be ruined by transaction forwards.
    It may run for a long time (hours) in the background, but can be stopped with the rebalancestop method.
    """
    if plugin.mutex.locked():
        return {"message": "Rebalance is already running, this may take a while. To stop it use the cli method 'rebalancestop'."}
    plugin.feeratio = float(feeratio)
    plugin.min_amount = Millisatoshi(min_amount)
    t = Thread(target=rebalanceall_thread, args=(plugin, ))
    t.start()
    return {"message": "Rebalance started"}
Ejemplo n.º 14
0
def maybe_adjust_fees(plugin: Plugin, scids: list):
    channels_adjusted = 0
    for scid in scids:
        our = plugin.adj_balances[scid]["our"]
        total = plugin.adj_balances[scid]["total"]
        percentage = our / total
        base = plugin.adj_basefee
        ppm = plugin.adj_ppmfee

        # select ideal values per channel
        fees = plugin.fee_strategy(plugin, scid)
        if fees is not None:
            base = fees['base']
            ppm = fees['ppm']

        # reset to normal fees if imbalance is not high enough
        if (percentage > plugin.imbalance and percentage < 1 - plugin.imbalance):
            if setchannelfee(plugin, scid, base, ppm):
                plugin.log(f"Set default fees as imbalance is too low: {scid}")
                plugin.adj_balances[scid]["last_liquidity"] = our
                channels_adjusted += 1
            continue

        if not significant_update(plugin, scid):
            continue

        percentage = get_adjusted_percentage(plugin, scid)
        assert 0 <= percentage and percentage <= 1
        ratio = plugin.get_ratio(percentage)
        if setchannelfee(plugin, scid, int(base), int(ppm * ratio)):
            plugin.log(f"Adjusted fees of {scid} with a ratio of {ratio}")
            plugin.adj_balances[scid]["last_liquidity"] = our
            channels_adjusted += 1
    return channels_adjusted
Ejemplo n.º 15
0
def rebalancestop(plugin: Plugin):
    """It stops the ongoing rebalanceall.
    """
    if not plugin.mutex.locked():
        if plugin.rebalanceall_msg is None:
            return {"message": "No rebalance is running, nothing to stop."}
        return {
            "message":
            f"No rebalance is running, nothing to stop. "
            f"Last 'rebalanceall' gave: {plugin.rebalanceall_msg}"
        }
    plugin.rebalance_stop = True
    plugin.mutex.acquire(blocking=True)
    plugin.rebalance_stop = False
    plugin.mutex.release()
    return {"message": plugin.rebalanceall_msg}
Ejemplo n.º 16
0
def getroute_iterative(plugin: Plugin, targetid, fromid, excludes,
                       msatoshi: 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`.
    """
    try:
        return plugin.rpc.getroute(targetid,
                                   fromid=fromid,
                                   exclude=excludes,
                                   msatoshi=msatoshi * plugin.msatfactoridx,
                                   maxhops=plugin.maxhopidx,
                                   riskfactor=10,
                                   cltv=9)
    except RpcError as e:
        # could not find route -> change params and restart loop
        if e.method == "getroute" and e.error.get('code') == 205:
            # reduce _msatfactor to look for smaller channels now
            plugin.msatfactoridx -= 1
            if plugin.msatfactoridx < 1:
                # when we reached neutral msat factor:
                # increase _maxhops and restart with msatfactor
                plugin.maxhopidx += 1
                plugin.msatfactoridx = plugin.msatfactor
            # abort if we reached maxhop limit
            if plugin.maxhops > 0 and plugin.maxhopidx > plugin.maxhops:
                raise NoRouteException
        raise e
Ejemplo n.º 17
0
def maybe_adjust_fees(plugin: Plugin, scids: list):
    channels_adjusted = 0
    for scid in scids:
        our = plugin.adj_balances[scid]["our"]
        total = plugin.adj_balances[scid]["total"]
        percentage = our / total

        # reset to normal fees if imbalance is not high enough
        if (percentage > plugin.imbalance
                and percentage < 1 - plugin.imbalance):
            if maybe_setchannelfee(plugin, scid, plugin.adj_basefee,
                                   plugin.adj_ppmfee):
                plugin.log(f"Set default fees as imbalance is too low: {scid}")
                plugin.adj_balances[scid]["last_liquidity"] = our
                channels_adjusted += 1
            continue

        if not significant_update(plugin, scid):
            continue

        percentage = get_adjusted_percentage(plugin, scid)
        assert 0 <= percentage and percentage <= 1
        ratio = plugin.get_ratio(percentage)
        if maybe_setchannelfee(plugin, scid, int(plugin.adj_basefee * ratio),
                               int(plugin.adj_ppmfee * ratio)):
            plugin.log(f"Adjusted fees of {scid} with a ratio of {ratio}")
            plugin.adj_balances[scid]["last_liquidity"] = our
            channels_adjusted += 1
    return channels_adjusted
Ejemplo n.º 18
0
def rebalanceall_thread(plugin: Plugin):
    if not plugin.mutex.acquire(blocking=False):
        return
    try:
        start_ts = time.time()
        feeadjuster_state = feeadjuster_toggle(plugin, False)
        channels = get_open_channels(plugin)
        plugin.enough_liquidity = get_enough_liquidity_threshold(channels)
        plugin.ideal_ratio = get_ideal_ratio(channels, plugin.enough_liquidity)
        plugin.log(
            f"Automatic rebalance is running with enough liquidity threshold: {plugin.enough_liquidity}, "
            f"ideal liquidity ratio: {plugin.ideal_ratio * 100:.2f}%, "
            f"min rebalancable amount: {plugin.min_amount}, "
            f"feeratio: {plugin.feeratio}")
        failed_channels = []
        success = 0
        fee_spent = Millisatoshi(0)
        while not plugin.rebalance_stop:
            result = maybe_rebalance_once(plugin, failed_channels)
            if not result["success"]:
                break
            success += 1
            fee_spent += result["fee_spent"]
        feeadjust_would_be_nice(plugin)
        feeadjuster_toggle(plugin, feeadjuster_state)
        elapsed_time = timedelta(seconds=time.time() - start_ts)
        plugin.rebalanceall_msg = f"Automatic rebalance finished: {success} successful rebalance, {fee_spent} fee spent, it took {str(elapsed_time)[:-3]}"
        plugin.log(plugin.rebalanceall_msg)
    finally:
        plugin.mutex.release()
Ejemplo n.º 19
0
def maybe_add_new_balances(plugin: Plugin, scids: list):
    for scid in scids:
        if scid not in plugin.adj_balances:
            chan = get_chan(plugin, scid)
            assert chan is not None
            plugin.adj_balances[scid] = {
                "our": int(chan["to_us_msat"]),
                "total": int(chan["total_msat"])
            }
Ejemplo n.º 20
0
def get_stub():
    plugin = Plugin()
    plugin.avail_interval = 60
    plugin.avail_window = 3600
    plugin.persist = {}
    plugin.persist['peerstate'] = {}
    plugin.persist['availcount'] = 0
    return plugin
Ejemplo n.º 21
0
def forward_event(plugin: Plugin, forward_event: dict, **kwargs):
    if not plugin.forward_event_subscription:
        return
    plugin.mutex.acquire(blocking=True)
    if forward_event["status"] == "settled":
        in_scid = forward_event["in_channel"]
        out_scid = forward_event["out_channel"]
        maybe_add_new_balances(plugin, [in_scid, out_scid])

        plugin.adj_balances[in_scid]["our"] += forward_event["in_msatoshi"]
        plugin.adj_balances[out_scid]["our"] -= forward_event["out_msatoshi"]
        try:
            # Pseudo-randomly add some hysterisis to the update
            if not plugin.deactivate_fuzz and random.randint(0, 9) == 9:
                time.sleep(random.randint(0, 5))
            maybe_adjust_fees(plugin, [in_scid, out_scid])
        except Exception as e:
            plugin.log("Adjusting fees: " + str(e), level="error")
    plugin.mutex.release()
Ejemplo n.º 22
0
def test_method_exceptions():
    """A bunch of tests that should fail calling the methods."""
    p = Plugin(autopatch=False)

    def fake_write_result(resultdict):
        global result_dict
        result_dict = resultdict

    @p.method("test_raise")
    def test_raise():
        raise RpcException("testing RpcException", code=-1000)

    req = Request(p, 1, "test_raise", {})
    req._write_result = fake_write_result
    p._dispatch_request(req)
    assert result_dict['jsonrpc'] == '2.0'
    assert result_dict['id'] == 1
    assert result_dict['error']['code'] == -1000
    assert result_dict['error']['message'] == "testing RpcException"

    @p.method("test_raise2")
    def test_raise2():
        raise Exception("normal exception")

    req = Request(p, 1, "test_raise2", {})
    req._write_result = fake_write_result
    p._dispatch_request(req)
    assert result_dict['jsonrpc'] == '2.0'
    assert result_dict['id'] == 1
    assert result_dict['error']['code'] == -32600
    assert result_dict['error'][
        'message'] == "Error while processing test_raise2: normal exception"
Ejemplo n.º 23
0
def feeadjust(plugin: Plugin):
    """Adjust fees for all existing channels.

    This method is automatically called in plugin init, or can be called manually after a successful payment.
    Otherwise, the plugin keeps the fees up-to-date.
    """
    plugin.mutex.acquire(blocking=True)
    peers = plugin.rpc.listpeers()["peers"]
    channels_adjusted = 0
    for peer in peers:
        for chan in peer["channels"]:
            if chan["state"] == "CHANNELD_NORMAL":
                scid = chan["short_channel_id"]
                plugin.adj_balances[scid] = {
                    "our": int(chan["to_us_msat"]),
                    "total": int(chan["total_msat"])
                }
                channels_adjusted += maybe_adjust_fees(plugin, [scid])
    msg = f"{channels_adjusted} channels adjusted"
    plugin.log(msg)
    plugin.mutex.release()
    return msg
Ejemplo n.º 24
0
def maybe_add_new_balances(plugin: Plugin, scids: list):
    for scid in scids:
        if scid not in plugin.adj_balances:
            # At this point we could call listchannels and pass the scid as
            # argument, and deduce the peer id (!our_id). But -as unlikely as
            # it is- we may not find it in our gossip (eg some corruption of
            # the gossip_store occured just before the forwarding event).
            # However, it MUST be present in a listpeers() entry.
            chan = get_chan(plugin, scid)
            assert chan is not None

            plugin.adj_balances[scid] = {
                "our": int(chan["to_us_msat"]),
                "total": int(chan["total_msat"])
            }
Ejemplo n.º 25
0
def get_fees_median(plugin: Plugin, scid: str):
    """ Median fees from peers or peer.

    The assumption is that our node competes in fees to other peers of a peer.
    """
    peer_id = get_peer_id_for_scid(plugin, scid)
    assert peer_id is not None
    if plugin.listchannels_by_dst:
        plugin.channels = plugin.rpc.call("listchannels",
                                          {"destination": peer_id})['channels']
    channels_to_peer = [ch for ch in plugin.channels
                        if ch['destination'] == peer_id
                        and ch['source'] != plugin.our_node_id]
    if len(channels_to_peer) == 0:
        return None
    fees_ppm = [ch['fee_per_millionth'] for ch in channels_to_peer]
    return {"base": plugin.adj_basefee, "ppm": statistics.median(fees_ppm)}
Ejemplo n.º 26
0
def read_channel_creation(plugin: Plugin):
    try:
        with open(plugin.data_location, 'r') as file:
            raw_data = json.load(file)
            plugin.channel_creation = ChannelCreation(
                status=Status[raw_data["status"]],
                id=raw_data["id"],
                private_key=raw_data["private_key"],
                redeem_script=raw_data["redeem_script"],
                private=raw_data["private"],
                invoice_amount=raw_data["invoice_amount"],
                inbound_percentage=raw_data["inbound_percentage"],
                invoice_label=raw_data["invoice_label"],
                preimage_hash=raw_data["preimage_hash"],
                address=raw_data["address"],
                expected_amount=raw_data["expected_amount"],
                bip21=raw_data["bip21"],
            )
            print("Read exiting channel creation state: {}".format(format_channel_creation(plugin.channel_creation)))

    except FileNotFoundError:
        print("Did not file existing channel creation state")
Ejemplo n.º 27
0
def test_duplicate_result():
    p = Plugin(autopatch=False)

    def test1(request):
        request.set_result(1)  # MARKER1
        request.set_result(1)

    req = Request(p, req_id=1, method="test1", params=[])
    ba = p._bind_kwargs(test1, {}, req)
    with pytest.raises(
            ValueError,
            match=r'current state is RequestState\.FINISHED(.*\n.*)*MARKER1'):
        test1(*ba.args)

    def test2(request):
        request.set_exception(1)  # MARKER2
        request.set_exception(1)

    req = Request(p, req_id=2, method="test2", params=[])
    ba = p._bind_kwargs(test2, {}, req)
    with pytest.raises(
            ValueError,
            match=r'current state is RequestState\.FAILED(.*\n*.*)*MARKER2'):
        test2(*ba.args)

    def test3(request):
        request.set_exception(1)  # MARKER3
        request.set_result(1)

    req = Request(p, req_id=3, method="test3", params=[])
    ba = p._bind_kwargs(test3, {}, req)
    with pytest.raises(
            ValueError,
            match=r'current state is RequestState\.FAILED(.*\n*.*)*MARKER3'):
        test3(*ba.args)

    def test4(request):
        request.set_result(1)  # MARKER4
        request.set_exception(1)

    req = Request(p, req_id=4, method="test4", params=[])
    ba = p._bind_kwargs(test4, {}, req)
    with pytest.raises(
            ValueError,
            match=r'current state is RequestState\.FINISHED(.*\n*.*)*MARKER4'):
        test4(*ba.args)
Ejemplo n.º 28
0
def rebalanceall_thread(plugin: Plugin):
    if not plugin.mutex.acquire(blocking=False):
        return
    try:
        channels = get_our_channels(plugin)
        plugin.enough_liquidity = get_enough_liquidity_threshold(channels)
        plugin.ideal_ratio = get_ideal_ratio(channels, plugin.enough_liquidity)
        plugin.log(f"Automatic rebalance is running with enough liquidity threshold: {plugin.enough_liquidity}, "
                   f"ideal liquidity ratio: {plugin.ideal_ratio * 100:.2f}%, "
                   f"min rebalancable amount: {plugin.min_amount}, "
                   f"feeratio: {plugin.feeratio}")
        failed_pairs = []
        success = 0
        fee_spent = Millisatoshi(0)
        while not plugin.rebalance_stop:
            result = maybe_rebalance_once(plugin, failed_pairs)
            if not result["success"]:
                break
            success += 1
            fee_spent += result["fee_spent"]
            feeadjust_would_be_nice(plugin)
        plugin.log(f"Automatic rebalance finished: {success} successful rebalance, {fee_spent} fee spent")
    finally:
        plugin.mutex.release()
Ejemplo n.º 29
0
from tqdm import tqdm
from typing import Mapping, Type, Iterator
from urllib.parse import urlparse
import json
import logging
import os
import re
import shutil
import struct
import sys
import sqlite3
import tempfile
import time
import psutil

plugin = Plugin()

root = logging.getLogger()
root.setLevel(logging.INFO)

handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(message)s')
handler.setFormatter(formatter)
root.addHandler(handler)

# A change that was proposed by c-lightning that needs saving to the
# backup. `version` is the database version before the transaction was
# applied. The optional snapshot reqpresents a complete copy of the database,
# as it was before applying the `transaction`. This is used by the plugin from
# time to time to allow the backend to compress the changelog and forms a new
Ejemplo n.º 30
0
 - -1 indicates that we were unable to find a route to the destination. This
    usually indicates that this is a leaf node that is currently offline.

 - 16399 is the code for unknown payment details and indicates a successful
   probe. The destination received the incoming payment but could not find a
   matching `payment_key`, which is expected since we generated the
   `payment_hash` at random :-)

"""
from datetime import datetime
from pyln.client import Plugin, RpcError
import random
import string

plugin = Plugin()


@plugin.method("probe")
def bin_search(plugin, node_id, capacity, epsilon, **kwargs):
    def loop(lower, upper, i):
        i += 1
        middle = (lower + upper) // 2
        error_code = failcode(node_id, middle)

        if (upper - lower) < epsilon:
            return middle, i

        elif error_code == 16399:
            return loop(middle, upper, i)