mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-22 22:45:27 +01:00
plugin: Split out the struct channel_hint
handling
We're getting serious about how we manage the channel_hints, so let's give them a proper home.
This commit is contained in:
parent
1144088e14
commit
37a204df41
5 changed files with 169 additions and 119 deletions
|
@ -27,8 +27,14 @@ PLUGIN_LIB_SRC := plugins/libplugin.c
|
||||||
PLUGIN_LIB_HEADER := plugins/libplugin.h
|
PLUGIN_LIB_HEADER := plugins/libplugin.h
|
||||||
PLUGIN_LIB_OBJS := $(PLUGIN_LIB_SRC:.c=.o)
|
PLUGIN_LIB_OBJS := $(PLUGIN_LIB_SRC:.c=.o)
|
||||||
|
|
||||||
PLUGIN_PAY_LIB_SRC := plugins/libplugin-pay.c
|
PLUGIN_PAY_LIB_SRC := \
|
||||||
PLUGIN_PAY_LIB_HEADER := plugins/libplugin-pay.h
|
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_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
|
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
|
||||||
|
|
99
plugins/channel_hint.c
Normal file
99
plugins/channel_hint.c
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
#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;
|
||||||
|
const jsmntok_t *payload = json_get_member(buffer, toks, "payload"),
|
||||||
|
*jhint =
|
||||||
|
json_get_member(buffer, payload, "channel_hint");
|
||||||
|
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
plugins/channel_hint.h
Normal file
61
plugins/channel_hint.h
Normal file
|
@ -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 */
|
|
@ -388,87 +388,6 @@ void payment_start(struct payment *p)
|
||||||
payment_start_at_blockheight(p, INVALID_BLOCKHEIGHT);
|
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
|
* Notify subscribers of the `channel_hint` topic about a changed hint
|
||||||
*
|
*
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <ccan/io/io.h>
|
#include <ccan/io/io.h>
|
||||||
#include <common/bolt11.h>
|
#include <common/bolt11.h>
|
||||||
#include <common/route.h>
|
#include <common/route.h>
|
||||||
|
#include <plugins/channel_hint.h>
|
||||||
#include <plugins/libplugin.h>
|
#include <plugins/libplugin.h>
|
||||||
#include <wire/onion_wire.h>
|
#include <wire/onion_wire.h>
|
||||||
|
|
||||||
|
@ -61,42 +62,6 @@ struct local_hint {
|
||||||
u16 htlc_budget;
|
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
|
/* 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
|
* the same order, and some modifiers are called with the payment, and the
|
||||||
* modifier's data before and after certain steps, allowing customization. The
|
* modifier's data before and after certain steps, allowing customization. The
|
||||||
|
|
Loading…
Add table
Reference in a new issue