Files
palladum-lightning/plugins/askrene/reserve.c
Rusty Russell 75616f6b77 common: add new_htable() macro to allocate, initialize and setup memleak coverage for any typed hash table.
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>
2025-10-24 11:30:17 +10:30

178 lines
4.6 KiB
C

#include "config.h"
#include <assert.h>
#include <ccan/htable/htable_type.h>
#include <ccan/tal/str/str.h>
#include <common/gossmap.h>
#include <common/json_stream.h>
#include <common/memleak.h>
#include <inttypes.h>
#include <plugins/askrene/askrene.h>
#include <plugins/askrene/reserve.h>
/* Note! We can have multiple of these! */
struct reserve {
/* What */
struct reserve_hop rhop;
/* When */
struct timemono timestamp;
/* ID of command which reserved it */
const char *cmd_id;
};
/* Hash table for reservations */
static const struct short_channel_id_dir *
reserve_scidd(const struct reserve *r)
{
return &r->rhop.scidd;
}
static bool reserve_eq_scidd(const struct reserve *r,
const struct short_channel_id_dir *scidd)
{
return short_channel_id_dir_eq(scidd, &r->rhop.scidd);
}
HTABLE_DEFINE_DUPS_TYPE(struct reserve, reserve_scidd, hash_scidd,
reserve_eq_scidd, reserve_htable);
struct reserve_htable *new_reserve_htable(const tal_t *ctx)
{
return new_htable(ctx, reserve_htable);
}
void reserve_add(struct reserve_htable *reserved,
const struct reserve_hop *rhop,
const char *cmd_id TAKES)
{
struct reserve *r = tal(reserved, struct reserve);
r->rhop = *rhop;
r->timestamp = time_mono();
r->cmd_id = tal_strdup(r, cmd_id);
reserve_htable_add(reserved, r);
}
bool reserve_remove(struct reserve_htable *reserved,
const struct reserve_hop *rhop)
{
struct reserve *r;
struct reserve_htable_iter rit;
/* Note! This may remove the "wrong" one, but since they're only
* differentiated for debugging, that's OK */
for (r = reserve_htable_getfirst(reserved, &rhop->scidd, &rit);
r;
r = reserve_htable_getnext(reserved, &rhop->scidd, &rit)) {
if (!amount_msat_eq(r->rhop.amount, rhop->amount))
continue;
reserve_htable_del(reserved, r);
tal_free(r);
return true;
}
return false;
}
void reserves_clear_capacities(struct reserve_htable *reserved,
const struct gossmap *gossmap,
fp16_t *capacities)
{
struct reserve *r;
struct reserve_htable_iter rit;
for (r = reserve_htable_first(reserved, &rit);
r;
r = reserve_htable_next(reserved, &rit)) {
struct gossmap_chan *c = gossmap_find_chan(gossmap, &r->rhop.scidd.scid);
size_t idx;
if (!c)
continue;
idx = gossmap_chan_idx(gossmap, c);
if (idx < tal_count(capacities))
capacities[idx] = 0;
}
}
void reserve_sub(const struct reserve_htable *reserved,
const struct short_channel_id_dir *scidd,
struct amount_msat *amount)
{
struct reserve *r;
struct reserve_htable_iter rit;
for (r = reserve_htable_getfirst(reserved, scidd, &rit);
r;
r = reserve_htable_getnext(reserved, scidd, &rit)) {
if (!amount_msat_sub(amount, *amount, r->rhop.amount))
*amount = AMOUNT_MSAT(0);
}
}
bool reserve_accumulate(const struct reserve_htable *reserved,
const struct short_channel_id_dir *scidd,
struct amount_msat *amount)
{
struct reserve *r;
struct reserve_htable_iter rit;
for (r = reserve_htable_getfirst(reserved, scidd, &rit);
r;
r = reserve_htable_getnext(reserved, scidd, &rit)) {
if (!amount_msat_add(amount, *amount, r->rhop.amount))
return false;
}
return true;
}
void json_add_reservations(struct json_stream *js,
const struct reserve_htable *reserved,
const char *fieldname)
{
struct reserve *r;
struct reserve_htable_iter rit;
json_array_start(js, fieldname);
for (r = reserve_htable_first(reserved, &rit);
r;
r = reserve_htable_next(reserved, &rit)) {
json_object_start(js, NULL);
json_add_short_channel_id_dir(js,
"short_channel_id_dir",
r->rhop.scidd);
json_add_amount_msat(js,
"amount_msat",
r->rhop.amount);
json_add_u64(js, "age_in_seconds",
timemono_between(time_mono(), r->timestamp).ts.tv_sec);
json_add_string(js, "command_id", r->cmd_id);
json_object_end(js);
}
json_array_end(js);
}
const char *fmt_reservations(const tal_t *ctx,
const struct reserve_htable *reserved,
const struct short_channel_id_dir *scidd)
{
struct reserve *r;
struct reserve_htable_iter rit;
char *ret = NULL;
for (r = reserve_htable_getfirst(reserved, scidd, &rit);
r;
r = reserve_htable_getnext(reserved, scidd, &rit)) {
u64 seconds;
if (!ret)
ret = tal_strdup(ctx, "");
else
tal_append_fmt(&ret, ", ");
tal_append_fmt(&ret, "%s by command %s",
fmt_amount_msat(tmpctx, r->rhop.amount), r->cmd_id);
seconds = timemono_between(time_mono(), r->timestamp).ts.tv_sec;
/* Add a note if it's old */
if (seconds > 0)
tal_append_fmt(&ret, " (%"PRIu64" seconds ago)", seconds);
}
return ret;
}