You can now simply add per-tal-object helpers for memleak, but our older pattern required calling memleak functions explicitly during memleak handling. Hash tables in particular need to be dynamically allocated (we override the allocators using htable_set_allocator and assume this), so it makes sense to have a helper macro that does all three. This eliminates a huge amount of code. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
228 lines
7.7 KiB
C
228 lines
7.7 KiB
C
#include "config.h"
|
|
#include <common/json_stream.h>
|
|
#include <common/memleak.h>
|
|
#include <plugins/channel_hint.h>
|
|
|
|
size_t channel_hint_hash(const struct short_channel_id_dir *out)
|
|
{
|
|
struct siphash24_ctx ctx;
|
|
siphash24_init(&ctx, siphash_seed());
|
|
siphash24_update(&ctx, &out->scid.u64, sizeof(u64));
|
|
siphash24_update(&ctx, &out->dir, sizeof(int));
|
|
return siphash24_done(&ctx);
|
|
}
|
|
|
|
const struct short_channel_id_dir *channel_hint_keyof(const struct channel_hint *out)
|
|
{
|
|
return &out->scid;
|
|
}
|
|
|
|
bool channel_hint_eq(const struct channel_hint *a,
|
|
const struct short_channel_id_dir *b)
|
|
{
|
|
return short_channel_id_eq(a->scid.scid, b->scid) &&
|
|
a->scid.dir == b->dir;
|
|
}
|
|
|
|
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, "total_capacity_msat", hint->capacity);
|
|
json_add_bool(dest, "enabled", hint->enabled);
|
|
json_object_end(dest);
|
|
}
|
|
|
|
/* How long until even a channel whose estimate is down at 0msat will
|
|
* be considered fully refilled. The refill rate is the inverse of
|
|
* this times the channel size. The refilling is a linear
|
|
* approximation, with a small hysteresis applied in order to prevent
|
|
* a single payment relaxing its own constraints thus causing it to
|
|
* prematurely retry an already attempted channel.
|
|
*/
|
|
#define PAY_REFILL_TIME 7200
|
|
|
|
/* Add an artificial delay before accepting updates. This ensures we
|
|
* don't actually end up relaxing a tight constraint inbetween the
|
|
* attempt that added it and the next retry. If we were to relax right
|
|
* away, then we could end up retrying the exact same path we just
|
|
* failed at. If the `time_between_attempts * refill > 1msat`, we'd
|
|
* end up not actually constraining at all, because we set the
|
|
* estimate to `attempt - 1msat`. This also results in the updates
|
|
* being limited to once every minute, and causes a stairway
|
|
* pattern. The hysteresis has to be >60s otherwise a single payment
|
|
* can already end up retrying a previously excluded channel.
|
|
*/
|
|
#define PAY_REFILL_HYSTERESIS 60
|
|
/**
|
|
* 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;
|
|
struct amount_msat capacity = hint->capacity;
|
|
|
|
if (now.ts.tv_sec < hint->timestamp + PAY_REFILL_HYSTERESIS)
|
|
return true;
|
|
|
|
u64 seconds = now.ts.tv_sec - hint->timestamp;
|
|
if (!amount_msat_mul(&refill, 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, capacity))
|
|
hint->estimated_capacity = 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(capacity, hint->estimated_capacity);
|
|
}
|
|
|
|
struct channel_hint *channel_hint_set_find(const struct channel_hint_set *self,
|
|
const struct short_channel_id_dir *scidd)
|
|
{
|
|
return channel_hint_map_get(self->hints, scidd);
|
|
}
|
|
|
|
/* See header */
|
|
struct channel_hint *
|
|
channel_hint_set_add(struct channel_hint_set *self, u32 timestamp,
|
|
const struct short_channel_id_dir *scidd, bool enabled,
|
|
const struct amount_msat *estimated_capacity,
|
|
const struct amount_msat capacity, u16 *htlc_budget)
|
|
{
|
|
struct channel_hint *copy, *old, *newhint;
|
|
|
|
/* If the channel is marked as enabled it must have an estimate. */
|
|
assert(!enabled || estimated_capacity != NULL);
|
|
|
|
/* If there was no hint, add the new one, if there was one,
|
|
* pick the one with the newer timestamp. */
|
|
old = channel_hint_set_find(self, scidd);
|
|
copy = tal_dup(tmpctx, struct channel_hint, old);
|
|
if (old == NULL) {
|
|
newhint = tal(self, struct channel_hint);
|
|
newhint->enabled = enabled;
|
|
newhint->scid = *scidd;
|
|
newhint->capacity = capacity;
|
|
if (estimated_capacity != NULL)
|
|
newhint->estimated_capacity = *estimated_capacity;
|
|
newhint->local = NULL;
|
|
newhint->timestamp = timestamp;
|
|
channel_hint_map_add(self->hints, newhint);
|
|
return newhint;
|
|
} else if (old->timestamp <= timestamp) {
|
|
/* `local` is kept, since we do not pass in those
|
|
* annotations here. */
|
|
old->enabled = enabled;
|
|
old->timestamp = timestamp;
|
|
if (estimated_capacity != NULL)
|
|
old->estimated_capacity = *estimated_capacity;
|
|
|
|
/* We always pick the larger of the capacities we are
|
|
* being told. This is because in some cases, such as
|
|
* routehints, we're not actually being told the total
|
|
* capacity, just lower values. */
|
|
if (amount_msat_greater(capacity, old->capacity))
|
|
old->capacity = capacity;
|
|
|
|
return copy;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 , *jhint;
|
|
struct channel_hint *hint = tal(ctx, struct channel_hint);
|
|
|
|
/* Deprecated API uses "payload" */
|
|
payload = json_get_member(buffer, toks, "payload");
|
|
/* Modern API includes fields directly */
|
|
if (!payload) {
|
|
jhint = json_get_member(buffer, toks, "channel_hint_update");
|
|
} else {
|
|
jhint = json_get_member(buffer, payload, "channel_hint");
|
|
}
|
|
ret = json_scan(ctx, buffer, jhint,
|
|
"{timestamp:%,scid:%,estimated_capacity_msat:%,total_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_msat, &hint->capacity),
|
|
JSON_SCAN(json_to_bool, &hint->enabled));
|
|
|
|
if (ret != NULL)
|
|
hint = tal_free(hint);
|
|
return hint;
|
|
}
|
|
|
|
struct channel_hint_set *channel_hint_set_new(const tal_t *ctx)
|
|
{
|
|
struct channel_hint_set *set = tal(ctx, struct channel_hint_set);
|
|
set->hints = new_htable(set, channel_hint_map);
|
|
return set;
|
|
}
|
|
|
|
void channel_hint_set_update(struct channel_hint_set *set,
|
|
const struct timeabs now)
|
|
{
|
|
struct channel_hint *hint;
|
|
struct channel_hint_map_iter iter;
|
|
for (hint = channel_hint_map_first(set->hints, &iter);
|
|
hint;
|
|
hint = channel_hint_map_next(set->hints, &iter))
|
|
channel_hint_update(now, hint);
|
|
}
|
|
|
|
size_t channel_hint_set_count(const struct channel_hint_set *set)
|
|
{
|
|
return channel_hint_map_count(set->hints);
|
|
}
|