askrene: rework constraints to exist in pairs.

This is a bit more efficient, but moreover the JSONRPC API is more
logical this way.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2024-10-04 08:59:53 +09:30
parent 3c5c22b17a
commit 321ec0875f
9 changed files with 75 additions and 103 deletions

View file

@ -727,13 +727,13 @@
"maximum_msat": {
"type": "msat",
"description": [
"The maximum value which this channel could pass. This or *minimum_msat* will be present, but not both."
"The maximum value which this channel could pass."
]
},
"minimum_msat": {
"type": "msat",
"description": [
"The minimum value which this channel could pass. This or *minimum_msat* will be present, but not both."
"The minimum value which this channel could pass."
]
}
}
@ -909,13 +909,13 @@
"maximum_msat": {
"type": "msat",
"description": [
"The maximum value which this channel could pass. This or *minimum_msat* will be present, but not both."
"The maximum value which this channel could pass."
]
},
"minimum_msat": {
"type": "msat",
"description": [
"The minimum value which this channel could pass. This or *minimum_msat* will be present, but not both."
"The minimum value which this channel could pass."
]
}
}

View file

@ -70,13 +70,13 @@
"maximum_msat": {
"type": "msat",
"description": [
"The maximum value which this channel could pass. This or *minimum_msat* will be present, but not both."
"The maximum value which this channel could pass."
]
},
"minimum_msat": {
"type": "msat",
"description": [
"The minimum value which this channel could pass. This or *minimum_msat* will be present, but not both."
"The minimum value which this channel could pass."
]
}
}

View file

@ -152,13 +152,13 @@
"maximum_msat": {
"type": "msat",
"description": [
"The maximum value which this channel could pass. This or *minimum_msat* will be present, but not both."
"The maximum value which this channel could pass."
]
},
"minimum_msat": {
"type": "msat",
"description": [
"The minimum value which this channel could pass. This or *minimum_msat* will be present, but not both."
"The minimum value which this channel could pass."
]
}
}

View file

@ -35,12 +35,6 @@ per_htlc_cost_key(const struct per_htlc_cost *phc)
return &phc->scidd;
}
static size_t hash_scidd(const struct short_channel_id_dir *scidd)
{
/* scids cost money to generate, so simple hash works here */
return (scidd->scid.u64 >> 32) ^ (scidd->scid.u64 << 1) ^ scidd->dir;
}
static inline bool per_htlc_cost_eq_key(const struct per_htlc_cost *phc,
const struct short_channel_id_dir *scidd)
{
@ -462,15 +456,17 @@ void get_constraints(const struct route_query *rq,
scidd.dir = dir;
*max = AMOUNT_MSAT(-1ULL);
/* Look through layers for any constraints */
/* Look through layers for any constraints (might be dummy
* ones, for created channels!) */
for (size_t i = 0; i < tal_count(rq->layers); i++) {
const struct constraint *cmin, *cmax;
cmin = layer_find_constraint(rq->layers[i], &scidd, CONSTRAINT_MIN);
if (cmin && amount_msat_greater(cmin->limit, *min))
*min = cmin->limit;
cmax = layer_find_constraint(rq->layers[i], &scidd, CONSTRAINT_MAX);
if (cmax && amount_msat_less(cmax->limit, *max))
*max = cmax->limit;
const struct constraint *c;
c = layer_find_constraint(rq->layers[i], &scidd);
if (c) {
if (amount_msat_greater(c->min, *min))
*min = c->min;
if (amount_msat_less(c->max, *max))
*max = c->max;
}
}
/* Might be here because it's reserved, but capacity is normal. */
@ -617,8 +613,7 @@ static void add_localchan(struct gossmap_localmods *mods,
}
/* Known capacity on local channels (ts = max) */
layer_update_constraint(info->local_layer, scidd, CONSTRAINT_MIN, UINT64_MAX, spendable);
layer_update_constraint(info->local_layer, scidd, CONSTRAINT_MAX, UINT64_MAX, spendable);
layer_update_constraint(info->local_layer, scidd, UINT64_MAX, &spendable, &spendable);
}
static struct command_result *
@ -838,16 +833,16 @@ static struct command_result *json_askrene_inform_channel(struct command *cmd,
"Amount overflow with reserves");
if (command_check_only(cmd))
return command_check_done(cmd);
c = layer_update_constraint(layer, scidd, CONSTRAINT_MAX,
time_now().ts.tv_sec, *amount);
c = layer_update_constraint(layer, scidd, time_now().ts.tv_sec,
NULL, amount);
goto output;
case INFORM_UNCONSTRAINED:
/* It passed, so the capacity is at least this much (minimal assumption is
* that no reserves were used) */
if (command_check_only(cmd))
return command_check_done(cmd);
c = layer_update_constraint(layer, scidd, CONSTRAINT_MIN,
time_now().ts.tv_sec, *amount);
c = layer_update_constraint(layer, scidd, time_now().ts.tv_sec,
amount, NULL);
goto output;
case INFORM_SUCCEEDED:
/* FIXME: We could do something useful here! */

View file

@ -74,4 +74,11 @@ static inline struct askrene *get_askrene(struct plugin *plugin)
return plugin_get_data(plugin, struct askrene);
}
/* Convenience routine for hash tables */
static inline size_t hash_scidd(const struct short_channel_id_dir *scidd)
{
/* scids cost money to generate, so simple hash works here */
return (scidd->scid.u64 >> 32) ^ (scidd->scid.u64 >> 16) ^ (scidd->scid.u64 << 1) ^ scidd->dir;
}
#endif /* LIGHTNING_PLUGINS_ASKRENE_ASKRENE_H */

View file

@ -25,27 +25,20 @@ struct local_channel {
} half[2];
};
static const struct constraint_key *
constraint_key(const struct constraint *c)
static const struct short_channel_id_dir *
constraint_scidd(const struct constraint *c)
{
return &c->key;
return &c->scidd;
}
static size_t hash_constraint_key(const struct constraint_key *key)
static inline bool constraint_eq_scidd(const struct constraint *c,
const struct short_channel_id_dir *scidd)
{
/* scids cost money to generate, so simple hash works here */
return (key->scidd.scid.u64 >> 32) ^ (key->scidd.scid.u64)
^ (key->scidd.dir << 1) ^ (key->type);
return short_channel_id_dir_eq(scidd, &c->scidd);
}
static inline bool constraint_eq_key(const struct constraint *c,
const struct constraint_key *key)
{
return short_channel_id_dir_eq(&key->scidd, &c->key.scidd) && key->type == c->key.type;
}
HTABLE_DEFINE_TYPE(struct constraint, constraint_key, hash_constraint_key,
constraint_eq_key, constraint_hash);
HTABLE_DEFINE_TYPE(struct constraint, constraint_scidd, hash_scidd,
constraint_eq_scidd, constraint_hash);
static struct short_channel_id
local_channel_scid(const struct local_channel *lc)
@ -208,7 +201,7 @@ void layer_update_local_channel(struct layer *layer,
* lookups. You can tell it's a fake one by the timestamp. */
scidd.scid = scid;
scidd.dir = dir;
layer_update_constraint(layer, &scidd, CONSTRAINT_MAX, UINT64_MAX, capacity);
layer_update_constraint(layer, &scidd, UINT64_MAX, NULL, &capacity);
}
struct amount_msat local_channel_capacity(const struct local_channel *lc)
@ -223,48 +216,41 @@ const struct local_channel *layer_find_local_channel(const struct layer *layer,
}
static struct constraint *layer_find_constraint_nonconst(const struct layer *layer,
const struct short_channel_id_dir *scidd,
enum constraint_type type)
const struct short_channel_id_dir *scidd)
{
struct constraint_key k = { *scidd, type };
return constraint_hash_get(layer->constraints, &k);
return constraint_hash_get(layer->constraints, scidd);
}
/* Public one returns const */
const struct constraint *layer_find_constraint(const struct layer *layer,
const struct short_channel_id_dir *scidd,
enum constraint_type type)
const struct short_channel_id_dir *scidd)
{
return layer_find_constraint_nonconst(layer, scidd, type);
return layer_find_constraint_nonconst(layer, scidd);
}
const struct constraint *layer_update_constraint(struct layer *layer,
const struct short_channel_id_dir *scidd,
enum constraint_type type,
u64 timestamp,
struct amount_msat limit)
const struct amount_msat *min,
const struct amount_msat *max)
{
struct constraint *c = layer_find_constraint_nonconst(layer, scidd, type);
struct constraint *c = layer_find_constraint_nonconst(layer, scidd);
if (!c) {
c = tal(layer, struct constraint);
c->key.scidd = *scidd;
c->key.type = type;
c->limit = limit;
c->scidd = *scidd;
c->min = AMOUNT_MSAT(0);
c->max = AMOUNT_MSAT(UINT64_MAX);
constraint_hash_add(layer->constraints, c);
} else {
switch (type) {
case CONSTRAINT_MIN:
/* Increase minimum? */
if (amount_msat_greater(limit, c->limit))
c->limit = limit;
break;
case CONSTRAINT_MAX:
/* Decrease maximum? */
if (amount_msat_less(limit, c->limit))
c->limit = limit;
break;
}
}
/* Increase minimum? */
if (min && amount_msat_greater(*min, c->min))
c->min = *min;
/* Decrease maximum? */
if (max && amount_msat_less(*max, c->max))
c->max = *max;
c->timestamp = timestamp;
return c;
}
@ -279,7 +265,7 @@ void layer_clear_overridden_capacities(const struct layer *layer,
for (con = constraint_hash_first(layer->constraints, &conit);
con;
con = constraint_hash_next(layer->constraints, &conit)) {
struct gossmap_chan *c = gossmap_find_chan(gossmap, &con->key.scidd.scid);
struct gossmap_chan *c = gossmap_find_chan(gossmap, &con->scidd.scid);
size_t idx;
if (!c)
continue;
@ -425,16 +411,12 @@ void json_add_constraint(struct json_stream *js,
json_object_start(js, fieldname);
if (layer)
json_add_string(js, "layer", layer->name);
json_add_short_channel_id_dir(js, "short_channel_id_dir", c->key.scidd);
json_add_short_channel_id_dir(js, "short_channel_id_dir", c->scidd);
json_add_u64(js, "timestamp", c->timestamp);
switch (c->key.type) {
case CONSTRAINT_MIN:
json_add_amount_msat(js, "minimum_msat", c->limit);
break;
case CONSTRAINT_MAX:
json_add_amount_msat(js, "maximum_msat", c->limit);
break;
}
if (!amount_msat_is_zero(c->min))
json_add_amount_msat(js, "minimum_msat", c->min);
if (!amount_msat_eq(c->max, AMOUNT_MSAT(UINT64_MAX)))
json_add_amount_msat(js, "maximum_msat", c->max);
json_object_end(js);
}

View file

@ -17,22 +17,15 @@ struct askrene;
struct layer;
struct json_stream;
enum constraint_type {
CONSTRAINT_MIN,
CONSTRAINT_MAX,
};
struct constraint_key {
struct short_channel_id_dir scidd;
enum constraint_type type;
};
/* A constraint reflects something we learned about a channel */
struct constraint {
struct constraint_key key;
struct short_channel_id_dir scidd;
/* Time this constraint was last updated */
u64 timestamp;
struct amount_msat limit;
/* Non-zero means set */
struct amount_msat min;
/* Non-0xFFFFF.... means set */
struct amount_msat max;
};
/* Look up a layer by name. */
@ -80,15 +73,14 @@ void layer_clear_overridden_capacities(const struct layer *layer,
/* Find a constraint in a layer. */
const struct constraint *layer_find_constraint(const struct layer *layer,
const struct short_channel_id_dir *scidd,
enum constraint_type type);
const struct short_channel_id_dir *scidd);
/* Add/update a constraint on a layer. */
/* Add/update one or more constraints on a layer. */
const struct constraint *layer_update_constraint(struct layer *layer,
const struct short_channel_id_dir *scidd,
enum constraint_type type,
u64 timestamp,
struct amount_msat limit);
const struct amount_msat *min,
const struct amount_msat *max);
/* Add local channels from this layer. zero_cost means set fees and delay to 0. */
void layer_add_localmods(const struct layer *layer,

View file

@ -25,12 +25,6 @@ reserve_scidd(const struct reserve *r)
return &r->rhop.scidd;
}
static size_t hash_scidd(const struct short_channel_id_dir *scidd)
{
/* scids cost money to generate, so simple hash works here */
return (scidd->scid.u64 >> 32) ^ (scidd->scid.u64 >> 16) ^ (scidd->scid.u64 << 1) ^ scidd->dir;
}
static bool reserve_eq_scidd(const struct reserve *r,
const struct short_channel_id_dir *scidd)
{

View file

@ -161,7 +161,9 @@ def test_layers(node_factory):
100000,
'unconstrained')
last_timestamp = int(time.time()) + 1
# Maximum for created channels is the real capacity.
expect['constraints'].append({'short_channel_id_dir': '0x0x1/1',
'maximum_msat': 1000000000,
'minimum_msat': 100000})
# Check timestamp first.
listlayers = l2.rpc.askrene_listlayers('test_layers')