Skip to content

Commit

Permalink
plugin: Split out the struct channel_hint handling
Browse files Browse the repository at this point in the history
We're getting serious about how we manage the channel_hints, so let's
give them a proper home.
  • Loading branch information
cdecker committed Aug 7, 2024
1 parent dc846dc commit e6d5334
Show file tree
Hide file tree
Showing 6 changed files with 170 additions and 121 deletions.
10 changes: 8 additions & 2 deletions plugins/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,14 @@ PLUGIN_LIB_SRC := plugins/libplugin.c
PLUGIN_LIB_HEADER := plugins/libplugin.h
PLUGIN_LIB_OBJS := $(PLUGIN_LIB_SRC:.c=.o)

PLUGIN_PAY_LIB_SRC := plugins/libplugin-pay.c
PLUGIN_PAY_LIB_HEADER := plugins/libplugin-pay.h
PLUGIN_PAY_LIB_SRC := \
plugins/channel_hint.c \
plugins/libplugin-pay.c

PLUGIN_PAY_LIB_HEADER := \
plugins/channel_hint.h \
plugins/libplugin-pay.h

PLUGIN_PAY_LIB_OBJS := $(PLUGIN_PAY_LIB_SRC:.c=.o)

PLUGIN_OFFERS_SRC := plugins/offers.c plugins/offers_offer.c plugins/offers_invreq_hook.c plugins/offers_inv_hook.c plugins/establish_onion_path.c plugins/fetchinvoice.c
Expand Down
96 changes: 96 additions & 0 deletions plugins/channel_hint.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#include "config.h"
#include <plugins/channel_hint.h>

void channel_hint_to_json(const char *name, const struct channel_hint *hint,
struct json_stream *dest)
{
json_object_start(dest, name);
json_add_u32(dest, "timestamp", hint->timestamp);
json_add_short_channel_id_dir(dest, "scid", hint->scid);
json_add_amount_msat(dest, "estimated_capacity_msat",
hint->estimated_capacity);
json_add_amount_msat(dest, "overall_capacity_msat",
hint->overall_capacity);
json_add_bool(dest, "enabled", hint->enabled);
json_object_end(dest);
}

#define PAY_REFILL_TIME 7200

/**
* Update the `channel_hint` in place, return whether it should be kept.
*
* This computes the refill-rate based on the overall capacity, and
* the time elapsed since the last update and relaxes the upper bound
* on the capacity, and resets the enabled flag if appropriate. If the
* hint is no longer useful, i.e., it does not provide any additional
* information on top of the structural information we've learned from
* the gossip, then we return `false` to signal that the
* `channel_hint` may be removed.
*/
bool channel_hint_update(const struct timeabs now, struct channel_hint *hint)
{
/* Precision is not required here, so integer division is good
* enough. But keep the order such that we do not round down
* too much. We do so by first multiplying, before
* dividing. The formula is `current = last + delta_t *
* overall / refill_rate`.
*/
struct amount_msat refill;
u64 seconds = now.ts.tv_sec - hint->timestamp;
if (!amount_msat_mul(&refill, hint->overall_capacity, seconds))
abort();

refill = amount_msat_div(refill, PAY_REFILL_TIME);
if (!amount_msat_add(&hint->estimated_capacity,
hint->estimated_capacity, refill))
abort();

/* Clamp the value to the `overall_capacity` */
if (amount_msat_greater(hint->estimated_capacity,
hint->overall_capacity))
hint->estimated_capacity = hint->overall_capacity;

/* TODO This is rather coarse. We could map the disabled flag
to having 0msat capacity, and then relax from there. But it'd
likely be too slow of a relaxation.*/
if (seconds > 60)
hint->enabled = true;

/* Since we update in-place we should make sure that we can
* just call update again and the result is stable, if no time
* has passed. */
hint->timestamp = now.ts.tv_sec;

/* We report this hint as useless, if the hint does not
* restrict the channel, i.e., if it is enabled and the
* estimate is the same as the overall capacity. */
return !hint->enabled || amount_msat_greater(hint->overall_capacity,
hint->estimated_capacity);
}

/**
* Load a channel_hint from its JSON representation.
*
* @return The initialized `channel_hint` or `NULL` if we encountered a parsing
* error.
*/
/*
struct channel_hint *channel_hint_from_json(const tal_t *ctx,
const char *buffer,
const jsmntok_t *toks)
{
const char *ret;
struct channel_hint *hint = tal(ctx, struct channel_hint);
ret = json_scan(ctx, buffer, toks,
"{timestamp:%,scid:%,estimated_capacity_msat:%,overall_capacity_msat:%,enabled:%}",
JSON_SCAN(json_to_u32, &hint->timestamp),
JSON_SCAN(json_to_short_channel_id_dir, &hint->scid),
JSON_SCAN(json_to_msat, &hint->estimated_capacity),
JSON_SCAN(json_to_bool, &hint->enabled));
if (ret != NULL)
hint = tal_free(hint);
return hint;
}
*/
61 changes: 61 additions & 0 deletions plugins/channel_hint.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#ifndef LIGHTNING_PLUGINS_CHANNEL_HINT_H
#define LIGHTNING_PLUGINS_CHANNEL_HINT_H

#include "config.h"
#include <bitcoin/short_channel_id.h>
#include <ccan/short_types/short_types.h>
#include <ccan/time/time.h>
#include <common/amount.h>
#include <common/json_stream.h>
#include <plugins/libplugin-pay.h>
#include <plugins/libplugin.h>

/* Information about channels we inferred from a) looking at our channels, and
* b) from failures encountered during attempts to perform a payment. These
* are attached to the root payment, since that information is
* global. Attempts update the estimated channel capacities when starting, and
* get remove on failure. Success keeps the capacities, since the capacities
* changed due to the successful HTLCs. */
struct channel_hint {
/* The timestamp this observation was made. Used to let the
* constraint expressed by this hint decay over time, until it
* is fully relaxed, at which point we can forget about it
* (the structural information is the best we can do in that
* case).
*/
u32 timestamp;
/* The short_channel_id we're going to use when referring to
* this channel. This can either be the real scid, or the
* local alias. The `pay` algorithm doesn't really care which
* one it is, but we'll prefer the scid as that's likely more
* readable than the alias. */
struct short_channel_id_dir scid;

/* Upper bound on remove channels inferred from payment failures. */
struct amount_msat estimated_capacity;

/* Is the channel enabled? */
bool enabled;

/* Non-null if we are one endpoint of this channel */
struct local_hint *local;

/* The total `amount_msat` that were used to fund the
* channel. This is always smaller gte the
* estimated_capacity */
struct amount_msat overall_capacity;
};

/* A collection of channel_hint instances, allowing us to handle and
* update them more easily. */
struct channel_hint_set {
/* tal_arr of channel_hints. */
struct channel_hint *hints;
};

bool channel_hint_update(const struct timeabs now,
struct channel_hint *hint);

void channel_hint_to_json(const char *name, const struct channel_hint *hint, struct json_stream *dest);

#endif /* LIGHTNING_PLUGINS_CHANNEL_HINT_H */
81 changes: 0 additions & 81 deletions plugins/libplugin-pay.c
Original file line number Diff line number Diff line change
Expand Up @@ -387,87 +387,6 @@ void payment_start(struct payment *p)
payment_start_at_blockheight(p, INVALID_BLOCKHEIGHT);
}

static void channel_hint_to_json(const char *name, const struct channel_hint *hint, struct json_stream *dest)
{
json_object_start(dest, name);
json_add_u32(dest, "timestamp", hint->timestamp);
json_add_short_channel_id_dir(dest, "scid", hint->scid);
json_add_amount_msat(dest, "estimated_capacity_msat", hint->estimated_capacity);
json_add_amount_msat(dest, "overall_capacity_msat", hint->overall_capacity);
json_add_bool(dest, "enabled", hint->enabled);
json_object_end(dest);
}

#define PAY_REFILL_TIME 7200

/**
* Update the `channel_hint` in place, return whether it should be kept.
*
* This computes the refill-rate based on the overall capacity, and
* the time elapsed since the last update and relaxes the upper bound
* on the capacity, and resets the enabled flag if appropriate. If the
* hint is no longer useful, i.e., it does not provide any additional
* information on top of the structural information we've learned from
* the gossip, then we return `false` to signal that the
* `channel_hint` may be removed.
*/
static bool channel_hint_update(const struct timeabs now,
struct channel_hint *hint)
{
/* Precision is not required here, so integer division is good enough.
*/
u64 refill_rate = hint->overall_capacity.millisatoshis / PAY_REFILL_TIME; /* Raw: just simpler */
u64 seconds = now.ts.tv_sec - hint->timestamp;
hint->estimated_capacity.millisatoshis += refill_rate * seconds; /* Raw: simpler */

/* Clamp the value to the `overall_capacity` */
if (amount_msat_greater(hint->estimated_capacity,
hint->overall_capacity))
hint->estimated_capacity = hint->overall_capacity;

/* TODO This is rather coarse. We could map the disabled flag
to having 0msat capacity, and then relax from there. But it'd
likely be too slow of a relaxation.*/
if (seconds > 60)
hint->enabled = true;

/* Since we update in-place we should make sure that we can
* just call update again and the result is stable, if no time
* has passed. */
hint->timestamp = now.ts.tv_sec;

/* We report this hint as useless, if the hint does not
* restrict the channel, i.e., if it is enabled and the
* estimate is the same as the overall capacity. */
return !hint->enabled || amount_msat_greater(hint->overall_capacity,
hint->estimated_capacity);
}

/**
* Load a channel_hint from its JSON representation.
*
* @return The initialized `channel_hint` or `NULL` if we encountered a parsing
* error.
*/
/*
static struct channel_hint *channel_hint_from_json(const tal_t *ctx,
const char *buffer,
const jsmntok_t *toks)
{
const char *ret;
struct channel_hint *hint = tal(ctx, struct channel_hint);
ret = json_scan(ctx, buffer, toks,
"{timestamp:%,scid:%,estimated_capacity_msat:%,overall_capacity_msat:%,enabled:%}",
JSON_SCAN(json_to_u32, &hint->timestamp),
JSON_SCAN(json_to_short_channel_id_dir, &hint->scid),
JSON_SCAN(json_to_msat, &hint->estimated_capacity),
JSON_SCAN(json_to_bool, &hint->enabled));
if (ret != NULL)
hint = tal_free(hint);
return hint;
}
*/
/**
* Notify subscribers of the `channel_hint` topic about a changed hint
*
Expand Down
37 changes: 1 addition & 36 deletions plugins/libplugin-pay.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <ccan/io/io.h>
#include <common/bolt11.h>
#include <common/route.h>
#include <plugins/channel_hint.h>
#include <plugins/libplugin.h>
#include <wire/onion_wire.h>

Expand Down Expand Up @@ -61,42 +62,6 @@ struct local_hint {
u16 htlc_budget;
};

/* Information about channels we inferred from a) looking at our channels, and
* b) from failures encountered during attempts to perform a payment. These
* are attached to the root payment, since that information is
* global. Attempts update the estimated channel capacities when starting, and
* get remove on failure. Success keeps the capacities, since the capacities
* changed due to the successful HTLCs. */
struct channel_hint {
/* The timestamp this observation was made. Used to let the
* constraint expressed by this hint decay over time, until it
* is fully relaxed, at which point we can forget about it
* (the structural information is the best we can do in that
* case).
*/
u32 timestamp;
/* The short_channel_id we're going to use when referring to
* this channel. This can either be the real scid, or the
* local alias. The `pay` algorithm doesn't really care which
* one it is, but we'll prefer the scid as that's likely more
* readable than the alias. */
struct short_channel_id_dir scid;

/* Upper bound on remove channels inferred from payment failures. */
struct amount_msat estimated_capacity;

/* Is the channel enabled? */
bool enabled;

/* Non-null if we are one endpoint of this channel */
struct local_hint *local;

/* The total `amount_msat` that were used to fund the
* channel. This is always smaller gte the
* estimated_capacity */
struct amount_msat overall_capacity;
};

/* Each payment goes through a number of steps that are always processed in
* the same order, and some modifiers are called with the payment, and the
* modifier's data before and after certain steps, allowing customization. The
Expand Down
6 changes: 4 additions & 2 deletions plugins/test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@ plugins/test/run-route-overlong: \
common/gossmap.o \
common/node_id.o \
common/route.o \
gossipd/gossip_store_wiregen.o
gossipd/gossip_store_wiregen.o \
plugins/channel_hint.o

plugins/test/run-route-calc: \
common/fp16.o \
common/gossmap.o \
common/node_id.o \
common/route.o \
gossipd/gossip_store_wiregen.o
gossipd/gossip_store_wiregen.o \
plugins/channel_hint.o

$(PLUGIN_TEST_PROGRAMS): $(BITCOIN_OBJS) $(WIRE_OBJS) $(PLUGIN_TEST_COMMON_OBJS)

Expand Down

0 comments on commit e6d5334

Please sign in to comment.