mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 09:54:16 +01:00
askrene: remember individual reservations, for better debugging.
Suggested-by: Lagrang3 <lagrang3@protonmail.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
10dc40a895
commit
0398d2ff73
@ -447,7 +447,6 @@ void get_constraints(const struct route_query *rq,
|
||||
struct amount_msat *max)
|
||||
{
|
||||
struct short_channel_id_dir scidd;
|
||||
const struct reserve *reserve;
|
||||
size_t idx = gossmap_chan_idx(rq->gossmap, chan);
|
||||
|
||||
*min = AMOUNT_MSAT(0);
|
||||
@ -494,14 +493,8 @@ void get_constraints(const struct route_query *rq,
|
||||
}
|
||||
|
||||
/* Finally, if any is in use, subtract that! */
|
||||
reserve = find_reserve(rq->reserved, &scidd);
|
||||
if (reserve) {
|
||||
/* They can definitely *try* to push too much through a channel! */
|
||||
if (!amount_msat_sub(min, *min, reserve->amount))
|
||||
*min = AMOUNT_MSAT(0);
|
||||
if (!amount_msat_sub(max, *max, reserve->amount))
|
||||
*max = AMOUNT_MSAT(0);
|
||||
}
|
||||
reserve_sub(rq->reserved, &scidd, min);
|
||||
reserve_sub(rq->reserved, &scidd, max);
|
||||
}
|
||||
|
||||
struct getroutes_info {
|
||||
@ -687,7 +680,6 @@ static struct command_result *json_askrene_reserve(struct command *cmd,
|
||||
{
|
||||
struct reserve_hop *path;
|
||||
struct json_stream *response;
|
||||
size_t num;
|
||||
struct askrene *askrene = get_askrene(cmd->plugin);
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
@ -695,16 +687,8 @@ static struct command_result *json_askrene_reserve(struct command *cmd,
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
num = reserves_add(askrene->reserved, path, tal_count(path));
|
||||
if (num != tal_count(path)) {
|
||||
const struct reserve *r = find_reserve(askrene->reserved, &path[num].scidd);
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"Overflow reserving %zu: %s amount %s (%s reserved already)",
|
||||
num,
|
||||
fmt_short_channel_id_dir(tmpctx, &path[num].scidd),
|
||||
fmt_amount_msat(tmpctx, path[num].amount),
|
||||
r ? fmt_amount_msat(tmpctx, r->amount) : "none");
|
||||
}
|
||||
for (size_t i = 0; i < tal_count(path); i++)
|
||||
reserve_add(askrene->reserved, &path[i], cmd->id);
|
||||
|
||||
response = jsonrpc_stream_success(cmd);
|
||||
return command_finished(cmd, response);
|
||||
@ -716,7 +700,6 @@ static struct command_result *json_askrene_unreserve(struct command *cmd,
|
||||
{
|
||||
struct reserve_hop *path;
|
||||
struct json_stream *response;
|
||||
size_t num;
|
||||
struct askrene *askrene = get_askrene(cmd->plugin);
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
@ -724,17 +707,14 @@ static struct command_result *json_askrene_unreserve(struct command *cmd,
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
num = reserves_remove(askrene->reserved, path, tal_count(path));
|
||||
if (num != tal_count(path)) {
|
||||
const struct reserve *r = find_reserve(askrene->reserved, &path[num].scidd);
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"Underflow unreserving %zu: %s amount %s (%zu reserved, amount %s)",
|
||||
num,
|
||||
fmt_short_channel_id_dir(tmpctx, &path[num].scidd),
|
||||
fmt_amount_msat(tmpctx, path[num].amount),
|
||||
r ? r->num_htlcs : 0,
|
||||
r ? fmt_amount_msat(tmpctx, r->amount) : "none");
|
||||
}
|
||||
for (size_t i = 0; i < tal_count(path); i++) {
|
||||
if (!reserve_remove(askrene->reserved, &path[i])) {
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"Unknown reservation for %s",
|
||||
fmt_short_channel_id_dir(tmpctx,
|
||||
&path[i].scidd));
|
||||
}
|
||||
}
|
||||
|
||||
response = jsonrpc_stream_success(cmd);
|
||||
return command_finished(cmd, response);
|
||||
|
@ -22,10 +22,8 @@ static void get_scidd(const struct gossmap *gossmap,
|
||||
|
||||
static void destroy_reservations(struct reserve_hop *rhops, struct askrene *askrene)
|
||||
{
|
||||
if (reserves_remove(askrene->reserved, rhops, tal_count(rhops))
|
||||
!= tal_count(rhops)) {
|
||||
plugin_err(askrene->plugin, "Failed to remove reservations?");
|
||||
}
|
||||
for (size_t i = 0; i < tal_count(rhops); i++)
|
||||
reserve_remove(askrene->reserved, &rhops[i]);
|
||||
}
|
||||
|
||||
static struct reserve_hop *new_reservations(const tal_t *ctx,
|
||||
@ -53,14 +51,7 @@ static void add_reservation(struct reserve_hop **reservations,
|
||||
get_scidd(rq->gossmap, flow, i, &rhop.scidd);
|
||||
rhop.amount = amt;
|
||||
|
||||
/* This should not happen, but simply don't reserve if it does */
|
||||
if (!reserves_add(askrene->reserved, &rhop, 1)) {
|
||||
plugin_log(rq->plugin, LOG_BROKEN,
|
||||
"Failed to reserve %s in %s",
|
||||
fmt_amount_msat(tmpctx, amt),
|
||||
fmt_short_channel_id_dir(tmpctx, &rhop.scidd));
|
||||
return;
|
||||
}
|
||||
reserve_add(askrene->reserved, &rhop, rq->cmd->id);
|
||||
|
||||
/* Set capacities entry to 0 so it get_constraints() looks in reserve. */
|
||||
idx = gossmap_chan_idx(rq->gossmap, flow->path[i]);
|
||||
|
@ -1,16 +1,27 @@
|
||||
#include "config.h"
|
||||
#include <assert.h>
|
||||
#include <ccan/htable/htable_type.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/gossmap.h>
|
||||
#include <common/memleak.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->scidd;
|
||||
return &r->rhop.scidd;
|
||||
}
|
||||
|
||||
static size_t hash_scidd(const struct short_channel_id_dir *scidd)
|
||||
@ -22,7 +33,7 @@ static size_t hash_scidd(const struct short_channel_id_dir *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->scidd);
|
||||
return short_channel_id_dir_eq(scidd, &r->rhop.scidd);
|
||||
}
|
||||
|
||||
HTABLE_DEFINE_TYPE(struct reserve, reserve_scidd, hash_scidd,
|
||||
@ -35,88 +46,37 @@ struct reserve_htable *new_reserve_htable(const tal_t *ctx)
|
||||
return reserved;
|
||||
}
|
||||
|
||||
/* Find a reservation for this scidd (if any!) */
|
||||
const struct reserve *find_reserve(const struct reserve_htable *reserved,
|
||||
const struct short_channel_id_dir *scidd)
|
||||
{
|
||||
return reserve_htable_get(reserved, scidd);
|
||||
}
|
||||
|
||||
/* Create a new (empty) reservation */
|
||||
static struct reserve *new_reserve(struct reserve_htable *reserved,
|
||||
const struct short_channel_id_dir *scidd)
|
||||
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->num_htlcs = 0;
|
||||
r->amount = AMOUNT_MSAT(0);
|
||||
r->scidd = *scidd;
|
||||
r->rhop = *rhop;
|
||||
r->timestamp = time_mono();
|
||||
r->cmd_id = tal_strdup(r, cmd_id);
|
||||
|
||||
reserve_htable_add(reserved, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void del_reserve(struct reserve_htable *reserved, struct reserve *r)
|
||||
bool reserve_remove(struct reserve_htable *reserved,
|
||||
const struct reserve_hop *rhop)
|
||||
{
|
||||
assert(r->num_htlcs == 0);
|
||||
struct reserve *r;
|
||||
struct reserve_htable_iter rit;
|
||||
|
||||
reserve_htable_del(reserved, r);
|
||||
tal_free(r);
|
||||
}
|
||||
/* 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;
|
||||
|
||||
/* Add to existing reservation (false if would overflow). */
|
||||
static bool add(struct reserve *r, struct amount_msat amount)
|
||||
{
|
||||
if (!amount_msat_accumulate(&r->amount, amount))
|
||||
return false;
|
||||
r->num_htlcs++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool remove(struct reserve *r, struct amount_msat amount)
|
||||
{
|
||||
if (r->num_htlcs == 0)
|
||||
return false;
|
||||
if (!amount_msat_sub(&r->amount, r->amount, amount))
|
||||
return false;
|
||||
r->num_htlcs--;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Atomically add to reserves, or fail.
|
||||
* Returns offset of failure, or num on success */
|
||||
size_t reserves_add(struct reserve_htable *reserved,
|
||||
const struct reserve_hop *hops,
|
||||
size_t num)
|
||||
{
|
||||
for (size_t i = 0; i < num; i++) {
|
||||
struct reserve *r = reserve_htable_get(reserved, &hops[i].scidd);
|
||||
if (!r)
|
||||
r = new_reserve(reserved, &hops[i].scidd);
|
||||
if (!add(r, hops[i].amount)) {
|
||||
reserves_remove(reserved, hops, i);
|
||||
return i;
|
||||
}
|
||||
reserve_htable_del(reserved, r);
|
||||
tal_free(r);
|
||||
return true;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
/* Atomically remove from reserves, to fail.
|
||||
* Returns offset of failure or tal_count(scidds) */
|
||||
size_t reserves_remove(struct reserve_htable *reserved,
|
||||
const struct reserve_hop *hops,
|
||||
size_t num)
|
||||
{
|
||||
for (size_t i = 0; i < num; i++) {
|
||||
struct reserve *r = reserve_htable_get(reserved, &hops[i].scidd);
|
||||
if (!r || !remove(r, hops[i].amount)) {
|
||||
reserves_add(reserved, hops, i);
|
||||
return i;
|
||||
}
|
||||
if (r->num_htlcs == 0)
|
||||
del_reserve(reserved, r);
|
||||
}
|
||||
return num;
|
||||
return false;
|
||||
}
|
||||
|
||||
void reserves_clear_capacities(struct reserve_htable *reserved,
|
||||
@ -129,7 +89,7 @@ void reserves_clear_capacities(struct reserve_htable *reserved,
|
||||
for (r = reserve_htable_first(reserved, &rit);
|
||||
r;
|
||||
r = reserve_htable_next(reserved, &rit)) {
|
||||
struct gossmap_chan *c = gossmap_find_chan(gossmap, &r->scidd.scid);
|
||||
struct gossmap_chan *c = gossmap_find_chan(gossmap, &r->rhop.scidd.scid);
|
||||
size_t idx;
|
||||
if (!c)
|
||||
continue;
|
||||
@ -139,6 +99,21 @@ void reserves_clear_capacities(struct reserve_htable *reserved,
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void reserve_memleak_mark(struct askrene *askrene, struct htable *memtable)
|
||||
{
|
||||
memleak_scan_htable(memtable, &askrene->reserved->raw);
|
||||
|
@ -8,43 +8,33 @@
|
||||
#include <common/amount.h>
|
||||
#include <common/fp16.h>
|
||||
|
||||
/* We reserve a path being used. This records how many and how much */
|
||||
struct reserve {
|
||||
size_t num_htlcs;
|
||||
struct short_channel_id_dir scidd;
|
||||
struct amount_msat amount;
|
||||
};
|
||||
|
||||
/* Initialize hash table for reservations */
|
||||
struct reserve_htable *new_reserve_htable(const tal_t *ctx);
|
||||
|
||||
/* Find a reservation for this scidd (if any!) */
|
||||
const struct reserve *find_reserve(const struct reserve_htable *reserved,
|
||||
const struct short_channel_id_dir *scidd);
|
||||
|
||||
|
||||
struct reserve_hop {
|
||||
struct short_channel_id_dir scidd;
|
||||
struct amount_msat amount;
|
||||
};
|
||||
|
||||
/* Atomically add to reserves, or fail.
|
||||
* Returns offset of failure, or num on success */
|
||||
size_t reserves_add(struct reserve_htable *reserved,
|
||||
const struct reserve_hop *hops,
|
||||
size_t num);
|
||||
/* Add a reservation */
|
||||
void reserve_add(struct reserve_htable *reserved,
|
||||
const struct reserve_hop *rhop,
|
||||
const char *cmd_id TAKES);
|
||||
|
||||
/* Atomically remove from reserves, to fail.
|
||||
* Returns offset of failure or tal_count(scidds) */
|
||||
size_t reserves_remove(struct reserve_htable *reserved,
|
||||
const struct reserve_hop *hops,
|
||||
size_t num);
|
||||
/* Try to remove a reservation, if it exists. */
|
||||
bool reserve_remove(struct reserve_htable *reserved,
|
||||
const struct reserve_hop *rhop);
|
||||
|
||||
/* Clear capacities array where we have reserves */
|
||||
void reserves_clear_capacities(struct reserve_htable *reserved,
|
||||
const struct gossmap *gossmap,
|
||||
fp16_t *capacities);
|
||||
|
||||
/* Subtract any reserves for scidd from this amount */
|
||||
void reserve_sub(const struct reserve_htable *reserved,
|
||||
const struct short_channel_id_dir *scidd,
|
||||
struct amount_msat *amount);
|
||||
|
||||
/* Scan for memleaks */
|
||||
void reserve_memleak_mark(struct askrene *askrene, struct htable *memtable);
|
||||
#endif /* LIGHTNING_PLUGINS_ASKRENE_RESERVE_H */
|
||||
|
Loading…
Reference in New Issue
Block a user