#include "config.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;
}
static void memleak_help_channel_hint_map(struct htable *memtable,
struct channel_hint_map *channel_hints)
{
memleak_scan_htable(memtable, &channel_hints->raw);
}
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);
}
#define PAY_REFILL_TIME 7200
#define PAY_REFILL_HYSTERESIS 60
bool channel_hint_update(const struct timeabs now, struct channel_hint *hint)
{
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();
if (amount_msat_greater(hint->estimated_capacity, capacity))
hint->estimated_capacity = capacity;
if (seconds > 60)
hint->enabled = true;
hint->timestamp = now.ts.tv_sec;
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);
}
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;
assert(!enabled || estimated_capacity != NULL);
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) {
old->enabled = enabled;
old->timestamp = timestamp;
if (estimated_capacity != NULL)
old->estimated_capacity = *estimated_capacity;
if (amount_msat_greater(capacity, old->capacity))
old->capacity = capacity;
return copy;
} else {
return NULL;
}
}
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, 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 = tal(set, struct channel_hint_map);
channel_hint_map_init(set->hints);
memleak_add_helper(set->hints, memleak_help_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);
}