mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-22 06:41:44 +01:00
askrene: add layers infrastructure.
These are the repositories of all information. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Header from folded patch 'layers-fixup.patch': fixup! askrene: add layers infrastructure.
This commit is contained in:
parent
ad209182ba
commit
d7f983a5b5
5 changed files with 575 additions and 18 deletions
|
@ -1,5 +1,5 @@
|
|||
PLUGIN_ASKRENE_SRC := plugins/askrene/askrene.c
|
||||
PLUGIN_ASKRENE_HEADER := plugins/askrene/askrene.h
|
||||
PLUGIN_ASKRENE_SRC := plugins/askrene/askrene.c plugins/askrene/layer.c
|
||||
PLUGIN_ASKRENE_HEADER := plugins/askrene/askrene.h plugins/askrene/layer.h
|
||||
PLUGIN_ASKRENE_OBJS := $(PLUGIN_ASKRENE_SRC:.c=.o)
|
||||
|
||||
$(PLUGIN_ASKRENE_OBJS): $(PLUGIN_ASKRENE_HEADER)
|
||||
|
|
|
@ -8,12 +8,14 @@
|
|||
*/
|
||||
#include "config.h"
|
||||
#include <ccan/array_size/array_size.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/gossmap.h>
|
||||
#include <common/json_param.h>
|
||||
#include <common/json_stream.h>
|
||||
#include <common/route.h>
|
||||
#include <errno.h>
|
||||
#include <plugins/askrene/askrene.h>
|
||||
#include <plugins/askrene/layer.h>
|
||||
#include <plugins/libplugin.h>
|
||||
|
||||
static struct askrene *get_askrene(struct plugin *plugin)
|
||||
|
@ -43,6 +45,24 @@ static struct command_result *param_string_array(struct command *cmd,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static struct command_result *param_known_layer(struct command *cmd,
|
||||
const char *name,
|
||||
const char *buffer,
|
||||
const jsmntok_t *tok,
|
||||
struct layer **layer)
|
||||
{
|
||||
const char *layername;
|
||||
struct command_result *ret = param_string(cmd, name, buffer, tok, &layername);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*layer = find_layer(get_askrene(cmd->plugin), layername);
|
||||
tal_free(layername);
|
||||
if (!*layer)
|
||||
return command_fail_badparam(cmd, name, buffer, tok, "Unknown layer");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool json_to_zero_or_one(const char *buffer, const jsmntok_t *tok, int *num)
|
||||
{
|
||||
u32 v32;
|
||||
|
@ -220,6 +240,8 @@ static struct command_result *json_askrene_create_channel(struct command *cmd,
|
|||
const jsmntok_t *params)
|
||||
{
|
||||
const char *layername;
|
||||
struct layer *layer;
|
||||
const struct local_channel *lc;
|
||||
struct node_id *src, *dst;
|
||||
struct short_channel_id *scid;
|
||||
struct amount_msat *capacity;
|
||||
|
@ -227,6 +249,7 @@ static struct command_result *json_askrene_create_channel(struct command *cmd,
|
|||
struct amount_msat *htlc_min, *htlc_max, *base_fee;
|
||||
u32 *proportional_fee;
|
||||
u16 *delay;
|
||||
struct askrene *askrene = get_askrene(cmd->plugin);
|
||||
|
||||
if (!param_check(cmd, buffer, params,
|
||||
p_req("layer", param_string, &layername),
|
||||
|
@ -242,9 +265,27 @@ static struct command_result *json_askrene_create_channel(struct command *cmd,
|
|||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
/* If it exists, it must match */
|
||||
layer = find_layer(askrene, layername);
|
||||
if (layer) {
|
||||
lc = layer_find_local_channel(layer, *scid);
|
||||
if (lc && !layer_check_local_channel(lc, src, dst, *capacity)) {
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"channel already exists with different values!");
|
||||
}
|
||||
} else
|
||||
lc = NULL;
|
||||
|
||||
if (command_check_only(cmd))
|
||||
return command_check_done(cmd);
|
||||
|
||||
if (!layer)
|
||||
layer = new_layer(askrene, layername);
|
||||
|
||||
layer_update_local_channel(layer, src, dst, *scid, *capacity,
|
||||
*base_fee, *proportional_fee, *delay,
|
||||
*htlc_min, *htlc_max);
|
||||
|
||||
response = jsonrpc_stream_success(cmd);
|
||||
return command_finished(cmd, response);
|
||||
}
|
||||
|
@ -253,11 +294,15 @@ static struct command_result *json_askrene_inform_channel(struct command *cmd,
|
|||
const char *buffer,
|
||||
const jsmntok_t *params)
|
||||
{
|
||||
struct layer *layer;
|
||||
const char *layername;
|
||||
struct short_channel_id *scid;
|
||||
int *direction;
|
||||
struct json_stream *response;
|
||||
struct amount_msat *max, *min;
|
||||
const struct constraint *c;
|
||||
struct short_channel_id_dir scidd;
|
||||
struct askrene *askrene = get_askrene(cmd->plugin);
|
||||
|
||||
if (!param_check(cmd, buffer, params,
|
||||
p_req("layer", param_string, &layername),
|
||||
|
@ -276,7 +321,23 @@ static struct command_result *json_askrene_inform_channel(struct command *cmd,
|
|||
if (command_check_only(cmd))
|
||||
return command_check_done(cmd);
|
||||
|
||||
layer = find_layer(askrene, layername);
|
||||
if (!layer)
|
||||
layer = new_layer(askrene, layername);
|
||||
|
||||
/* Calls expect a convenient short_channel_id_dir struct */
|
||||
scidd.scid = *scid;
|
||||
scidd.dir = *direction;
|
||||
|
||||
if (min) {
|
||||
c = layer_update_constraint(layer, &scidd, CONSTRAINT_MIN,
|
||||
time_now().ts.tv_sec, *min);
|
||||
} else {
|
||||
c = layer_update_constraint(layer, &scidd, CONSTRAINT_MAX,
|
||||
time_now().ts.tv_sec, *max);
|
||||
}
|
||||
response = jsonrpc_stream_success(cmd);
|
||||
json_add_constraint(response, "constraint", c, layer);
|
||||
return command_finished(cmd, response);
|
||||
}
|
||||
|
||||
|
@ -286,7 +347,9 @@ static struct command_result *json_askrene_disable_node(struct command *cmd,
|
|||
{
|
||||
struct node_id *node;
|
||||
const char *layername;
|
||||
struct layer *layer;
|
||||
struct json_stream *response;
|
||||
struct askrene *askrene = get_askrene(cmd->plugin);
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
p_req("layer", param_string, &layername),
|
||||
|
@ -294,6 +357,14 @@ static struct command_result *json_askrene_disable_node(struct command *cmd,
|
|||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
layer = find_layer(askrene, layername);
|
||||
if (!layer)
|
||||
layer = new_layer(askrene, layername);
|
||||
|
||||
/* We save this in the layer, because they want us to disable all the channels
|
||||
* to the node at *use* time (a new channel might be gossiped!). */
|
||||
layer_add_disabled_node(layer, node);
|
||||
|
||||
response = jsonrpc_stream_success(cmd);
|
||||
return command_finished(cmd, response);
|
||||
}
|
||||
|
@ -302,26 +373,17 @@ static struct command_result *json_askrene_listlayers(struct command *cmd,
|
|||
const char *buffer,
|
||||
const jsmntok_t *params)
|
||||
{
|
||||
struct askrene *askrene = get_askrene(cmd->plugin);
|
||||
const char *layername;
|
||||
struct json_stream *response;
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
p_req("layer", param_string, &layername),
|
||||
p_opt("layer", param_string, &layername),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
response = jsonrpc_stream_success(cmd);
|
||||
json_array_start(response, "layers");
|
||||
json_object_start(response, NULL);
|
||||
json_add_string(response, "layer", layername);
|
||||
json_array_start(response, "disabled_nodes");
|
||||
json_array_end(response);
|
||||
json_array_start(response, "created_channels");
|
||||
json_array_end(response);
|
||||
json_array_start(response, "capacities");
|
||||
json_array_end(response);
|
||||
json_object_end(response);
|
||||
json_array_end(response);
|
||||
json_add_layers(response, askrene, "layers", layername);
|
||||
return command_finished(cmd, response);
|
||||
}
|
||||
|
||||
|
@ -329,18 +391,22 @@ static struct command_result *json_askrene_age(struct command *cmd,
|
|||
const char *buffer,
|
||||
const jsmntok_t *params)
|
||||
{
|
||||
const char *layername;
|
||||
struct layer *layer;
|
||||
struct json_stream *response;
|
||||
u64 *cutoff;
|
||||
size_t num_removed;
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
p_req("layer", param_string, &layername),
|
||||
p_req("layer", param_known_layer, &layer),
|
||||
p_req("cutoff", param_u64, &cutoff),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
num_removed = layer_trim_constraints(layer, *cutoff);
|
||||
|
||||
response = jsonrpc_stream_success(cmd);
|
||||
json_add_string(response, "layer", layername);
|
||||
json_add_string(response, "layer", layer_name(layer));
|
||||
json_add_u64(response, "num_removed", num_removed);
|
||||
return command_finished(cmd, response);
|
||||
}
|
||||
|
||||
|
@ -379,11 +445,17 @@ static const struct plugin_command commands[] = {
|
|||
},
|
||||
};
|
||||
|
||||
static void askrene_markmem(struct plugin *plugin, struct htable *memtable)
|
||||
{
|
||||
layer_memleak_mark(get_askrene(plugin), memtable);
|
||||
}
|
||||
|
||||
static const char *init(struct plugin *plugin,
|
||||
const char *buf UNUSED, const jsmntok_t *config UNUSED)
|
||||
{
|
||||
struct askrene *askrene = tal(plugin, struct askrene);
|
||||
askrene->plugin = plugin;
|
||||
list_head_init(&askrene->layers);
|
||||
askrene->gossmap = gossmap_load(askrene, GOSSIP_STORE_FILENAME, NULL);
|
||||
|
||||
if (!askrene->gossmap)
|
||||
|
@ -391,7 +463,7 @@ static const char *init(struct plugin *plugin,
|
|||
GOSSIP_STORE_FILENAME, strerror(errno));
|
||||
|
||||
plugin_set_data(plugin, askrene);
|
||||
(void)get_askrene(plugin);
|
||||
plugin_set_memleak_handler(plugin, askrene_markmem);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#ifndef LIGHTNING_PLUGINS_ASKRENE_ASKRENE_H
|
||||
#define LIGHTNING_PLUGINS_ASKRENE_ASKRENE_H
|
||||
#include "config.h"
|
||||
#include <bitcoin/short_channel_id.h>
|
||||
#include <ccan/list/list.h>
|
||||
#include <common/amount.h>
|
||||
|
||||
/* We reserve a path being used. This records how many and how much */
|
||||
struct reserve {
|
||||
|
@ -21,6 +24,8 @@ struct route {
|
|||
struct askrene {
|
||||
struct plugin *plugin;
|
||||
struct gossmap *gossmap;
|
||||
/* List of layers */
|
||||
struct list_head layers;
|
||||
};
|
||||
|
||||
#endif /* LIGHTNING_PLUGINS_ASKRENE_ASKRENE_H */
|
||||
|
|
376
plugins/askrene/layer.c
Normal file
376
plugins/askrene/layer.c
Normal file
|
@ -0,0 +1,376 @@
|
|||
#include "config.h"
|
||||
#include <ccan/array_size/array_size.h>
|
||||
#include <ccan/htable/htable_type.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/json_stream.h>
|
||||
#include <common/memleak.h>
|
||||
#include <plugins/askrene/askrene.h>
|
||||
#include <plugins/askrene/layer.h>
|
||||
|
||||
/* A channels which doesn't (necessarily) exist in the gossmap. */
|
||||
struct local_channel {
|
||||
/* Canonical order, n1 < n2 */
|
||||
struct node_id n1, n2;
|
||||
struct short_channel_id scid;
|
||||
struct amount_msat capacity;
|
||||
|
||||
struct added_channel_half {
|
||||
/* Other fields only valid if this is true */
|
||||
bool enabled;
|
||||
u16 delay;
|
||||
u32 proportional_fee;
|
||||
struct amount_msat base_fee;
|
||||
struct amount_msat htlc_min, htlc_max;
|
||||
} half[2];
|
||||
};
|
||||
|
||||
static const struct constraint_key *
|
||||
constraint_key(const struct constraint *c)
|
||||
{
|
||||
return &c->key;
|
||||
}
|
||||
|
||||
static size_t hash_constraint_key(const struct constraint_key *key)
|
||||
{
|
||||
/* 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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
static struct short_channel_id
|
||||
local_channel_scid(const struct local_channel *lc)
|
||||
{
|
||||
return lc->scid;
|
||||
}
|
||||
|
||||
static size_t hash_scid(const struct short_channel_id scid)
|
||||
{
|
||||
/* scids cost money to generate, so simple hash works here */
|
||||
return (scid.u64 >> 32) ^ (scid.u64 >> 16) ^ scid.u64;
|
||||
}
|
||||
|
||||
static inline bool local_channel_eq_scid(const struct local_channel *lc,
|
||||
const struct short_channel_id scid)
|
||||
{
|
||||
return short_channel_id_eq(scid, lc->scid);
|
||||
}
|
||||
|
||||
HTABLE_DEFINE_TYPE(struct local_channel, local_channel_scid, hash_scid,
|
||||
local_channel_eq_scid, local_channel_hash);
|
||||
|
||||
struct layer {
|
||||
/* Inside global list of layers */
|
||||
struct list_node list;
|
||||
|
||||
/* Unique identifiers */
|
||||
const char *name;
|
||||
|
||||
/* Completely made up local additions, indexed by scid */
|
||||
struct local_channel_hash *local_channels;
|
||||
|
||||
/* Additional info, indexed by scid+dir */
|
||||
struct constraint_hash *constraints;
|
||||
|
||||
/* Nodes to completely disable (tal_arr) */
|
||||
struct node_id *disabled_nodes;
|
||||
};
|
||||
|
||||
struct layer *new_layer(struct askrene *askrene, const char *name)
|
||||
{
|
||||
struct layer *l = tal(askrene, struct layer);
|
||||
|
||||
l->name = tal_strdup(l, name);
|
||||
l->local_channels = tal(l, struct local_channel_hash);
|
||||
local_channel_hash_init(l->local_channels);
|
||||
l->constraints = tal(l, struct constraint_hash);
|
||||
constraint_hash_init(l->constraints);
|
||||
l->disabled_nodes = tal_arr(l, struct node_id, 0);
|
||||
|
||||
list_add(&askrene->layers, &l->list);
|
||||
return l;
|
||||
}
|
||||
|
||||
/* Swap if necessary to make into BOLT-7 order. Return direction. */
|
||||
static int canonicalize_node_order(const struct node_id **n1,
|
||||
const struct node_id **n2)
|
||||
{
|
||||
const struct node_id *tmp;
|
||||
|
||||
if (node_id_cmp(*n1, *n2) < 0)
|
||||
return 0;
|
||||
tmp = *n2;
|
||||
*n2 = *n1;
|
||||
*n1 = tmp;
|
||||
return 1;
|
||||
}
|
||||
|
||||
struct layer *find_layer(struct askrene *askrene, const char *name)
|
||||
{
|
||||
struct layer *l;
|
||||
list_for_each(&askrene->layers, l, list) {
|
||||
if (streq(l->name, name))
|
||||
return l;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *layer_name(const struct layer *layer)
|
||||
{
|
||||
return layer->name;
|
||||
}
|
||||
|
||||
static struct local_channel *new_local_channel(struct layer *layer,
|
||||
const struct node_id *n1,
|
||||
const struct node_id *n2,
|
||||
struct short_channel_id scid,
|
||||
struct amount_msat capacity)
|
||||
{
|
||||
struct local_channel *lc = tal(layer, struct local_channel);
|
||||
lc->n1 = *n1;
|
||||
lc->n2 = *n2;
|
||||
lc->scid = scid;
|
||||
lc->capacity = capacity;
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(lc->half); i++)
|
||||
lc->half[i].enabled = false;
|
||||
|
||||
local_channel_hash_add(layer->local_channels, lc);
|
||||
return lc;
|
||||
}
|
||||
|
||||
bool layer_check_local_channel(const struct local_channel *lc,
|
||||
const struct node_id *n1,
|
||||
const struct node_id *n2,
|
||||
struct amount_msat capacity)
|
||||
{
|
||||
canonicalize_node_order(&n1, &n2);
|
||||
return node_id_eq(&lc->n1, n1)
|
||||
&& node_id_eq(&lc->n2, n2)
|
||||
&& amount_msat_eq(lc->capacity, capacity);
|
||||
}
|
||||
|
||||
/* Update a local channel to a layer: fails if you try to change capacity or nodes! */
|
||||
void layer_update_local_channel(struct layer *layer,
|
||||
const struct node_id *src,
|
||||
const struct node_id *dst,
|
||||
struct short_channel_id scid,
|
||||
struct amount_msat capacity,
|
||||
struct amount_msat base_fee,
|
||||
u32 proportional_fee,
|
||||
u16 delay,
|
||||
struct amount_msat htlc_min,
|
||||
struct amount_msat htlc_max)
|
||||
{
|
||||
struct local_channel *lc = local_channel_hash_get(layer->local_channels, scid);
|
||||
int dir = canonicalize_node_order(&src, &dst);
|
||||
|
||||
if (lc) {
|
||||
assert(layer_check_local_channel(lc, src, dst, capacity));
|
||||
} else {
|
||||
lc = new_local_channel(layer, src, dst, scid, capacity);
|
||||
}
|
||||
|
||||
lc->half[dir].enabled = true;
|
||||
lc->half[dir].htlc_min = htlc_min;
|
||||
lc->half[dir].htlc_max = htlc_max;
|
||||
lc->half[dir].base_fee = base_fee;
|
||||
lc->half[dir].proportional_fee = proportional_fee;
|
||||
lc->half[dir].delay = delay;
|
||||
}
|
||||
|
||||
struct amount_msat local_channel_capacity(const struct local_channel *lc)
|
||||
{
|
||||
return lc->capacity;
|
||||
}
|
||||
|
||||
const struct local_channel *layer_find_local_channel(const struct layer *layer,
|
||||
struct short_channel_id scid)
|
||||
{
|
||||
return local_channel_hash_get(layer->local_channels, scid);
|
||||
}
|
||||
|
||||
static struct constraint *layer_find_constraint_nonconst(const struct layer *layer,
|
||||
const struct short_channel_id_dir *scidd,
|
||||
enum constraint_type type)
|
||||
{
|
||||
struct constraint_key k = { *scidd, type };
|
||||
return constraint_hash_get(layer->constraints, &k);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
return layer_find_constraint_nonconst(layer, scidd, type);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct constraint *c = layer_find_constraint_nonconst(layer, scidd, type);
|
||||
if (!c) {
|
||||
c = tal(layer, struct constraint);
|
||||
c->key.scidd = *scidd;
|
||||
c->key.type = type;
|
||||
c->limit = limit;
|
||||
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;
|
||||
}
|
||||
}
|
||||
c->timestamp = timestamp;
|
||||
return c;
|
||||
}
|
||||
|
||||
size_t layer_trim_constraints(struct layer *layer, u64 cutoff)
|
||||
{
|
||||
size_t num_removed = 0;
|
||||
struct constraint_hash_iter conit;
|
||||
struct constraint *con;
|
||||
|
||||
for (con = constraint_hash_first(layer->constraints, &conit);
|
||||
con;
|
||||
con = constraint_hash_next(layer->constraints, &conit)) {
|
||||
if (con->timestamp < cutoff) {
|
||||
constraint_hash_delval(layer->constraints, &conit);
|
||||
tal_free(con);
|
||||
num_removed++;
|
||||
}
|
||||
}
|
||||
return num_removed;
|
||||
}
|
||||
|
||||
void layer_add_disabled_node(struct layer *layer, const struct node_id *node)
|
||||
{
|
||||
tal_arr_expand(&layer->disabled_nodes, *node);
|
||||
}
|
||||
|
||||
static void json_add_local_channel(struct json_stream *response,
|
||||
const char *fieldname,
|
||||
const struct local_channel *lc,
|
||||
int dir)
|
||||
{
|
||||
json_object_start(response, fieldname);
|
||||
|
||||
if (dir == 0) {
|
||||
json_add_node_id(response, "source", &lc->n1);
|
||||
json_add_node_id(response, "destination", &lc->n2);
|
||||
} else {
|
||||
json_add_node_id(response, "source", &lc->n2);
|
||||
json_add_node_id(response, "destination", &lc->n1);
|
||||
}
|
||||
json_add_short_channel_id(response, "short_channel_id", lc->scid);
|
||||
json_add_amount_msat(response, "capacity_msat", lc->capacity);
|
||||
json_add_amount_msat(response, "htlc_minimum_msat", lc->half[dir].htlc_min);
|
||||
json_add_amount_msat(response, "htlc_maximum_msat", lc->half[dir].htlc_max);
|
||||
json_add_amount_msat(response, "fee_base_msat", lc->half[dir].base_fee);
|
||||
json_add_u32(response, "fee_proportional_millionths", lc->half[dir].proportional_fee);
|
||||
json_add_u32(response, "delay", lc->half[dir].delay);
|
||||
|
||||
json_object_end(response);
|
||||
}
|
||||
|
||||
void json_add_constraint(struct json_stream *js,
|
||||
const char *fieldname,
|
||||
const struct constraint *c,
|
||||
const struct layer *layer)
|
||||
{
|
||||
json_object_start(js, fieldname);
|
||||
if (layer)
|
||||
json_add_string(js, "layer", layer->name);
|
||||
json_add_short_channel_id(js, "short_channel_id", c->key.scidd.scid);
|
||||
json_add_u32(js, "direction", c->key.scidd.dir);
|
||||
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;
|
||||
}
|
||||
json_object_end(js);
|
||||
}
|
||||
|
||||
static void json_add_layer(struct json_stream *js,
|
||||
const char *fieldname,
|
||||
const struct layer *layer)
|
||||
{
|
||||
struct local_channel_hash_iter lcit;
|
||||
const struct local_channel *lc;
|
||||
struct constraint_hash_iter conit;
|
||||
const struct constraint *c;
|
||||
|
||||
json_object_start(js, fieldname);
|
||||
json_add_string(js, "layer", layer->name);
|
||||
json_array_start(js, "disabled_nodes");
|
||||
for (size_t i = 0; i < tal_count(layer->disabled_nodes); i++)
|
||||
json_add_node_id(js, NULL, &layer->disabled_nodes[i]);
|
||||
json_array_end(js);
|
||||
json_array_start(js, "created_channels");
|
||||
for (lc = local_channel_hash_first(layer->local_channels, &lcit);
|
||||
lc;
|
||||
lc = local_channel_hash_next(layer->local_channels, &lcit)) {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(lc->half); i++) {
|
||||
if (lc->half[i].enabled)
|
||||
json_add_local_channel(js, NULL, lc, i);
|
||||
}
|
||||
}
|
||||
json_array_end(js);
|
||||
json_array_start(js, "constraints");
|
||||
for (c = constraint_hash_first(layer->constraints, &conit);
|
||||
c;
|
||||
c = constraint_hash_next(layer->constraints, &conit)) {
|
||||
json_add_constraint(js, NULL, c, NULL);
|
||||
}
|
||||
json_array_end(js);
|
||||
json_object_end(js);
|
||||
}
|
||||
|
||||
void json_add_layers(struct json_stream *js,
|
||||
struct askrene *askrene,
|
||||
const char *fieldname,
|
||||
const char *layername)
|
||||
{
|
||||
struct layer *l;
|
||||
|
||||
json_array_start(js, fieldname);
|
||||
list_for_each(&askrene->layers, l, list) {
|
||||
if (layername && !streq(l->name, layername))
|
||||
continue;
|
||||
json_add_layer(js, NULL, l);
|
||||
}
|
||||
json_array_end(js);
|
||||
}
|
||||
|
||||
void layer_memleak_mark(struct askrene *askrene, struct htable *memtable)
|
||||
{
|
||||
struct layer *l;
|
||||
list_for_each(&askrene->layers, l, list) {
|
||||
memleak_scan_htable(memtable, &l->constraints->raw);
|
||||
memleak_scan_htable(memtable, &l->local_channels->raw);
|
||||
}
|
||||
}
|
104
plugins/askrene/layer.h
Normal file
104
plugins/askrene/layer.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
#ifndef LIGHTNING_PLUGINS_ASKRENE_LAYER_H
|
||||
#define LIGHTNING_PLUGINS_ASKRENE_LAYER_H
|
||||
/* A layer is the group of information maintained by askrene. The caller
|
||||
* specifies which layers to use when asking for a route, and tell askrene
|
||||
* what layer to add new information to.
|
||||
*
|
||||
* Layers can be used to shape local decisions (for this payment, add these
|
||||
* connections, or disable all connections to this node). You can also,
|
||||
* in theory, export a layer, or import a layer from another source, to see
|
||||
* what the results are when that layer is included. */
|
||||
#include "config.h"
|
||||
#include <bitcoin/short_channel_id.h>
|
||||
#include <common/amount.h>
|
||||
#include <common/node_id.h>
|
||||
|
||||
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;
|
||||
/* Time this constraint was last updated */
|
||||
u64 timestamp;
|
||||
struct amount_msat limit;
|
||||
};
|
||||
|
||||
/* Look up a layer by name. */
|
||||
struct layer *find_layer(struct askrene *askrene, const char *name);
|
||||
|
||||
/* Create new layer by name. */
|
||||
struct layer *new_layer(struct askrene *askrene, const char *name);
|
||||
|
||||
/* Get the name of the layer */
|
||||
const char *layer_name(const struct layer *layer);
|
||||
|
||||
/* Find a local channel in a layer */
|
||||
const struct local_channel *layer_find_local_channel(const struct layer *layer,
|
||||
struct short_channel_id scid);
|
||||
|
||||
/* Get capacity of that channel. */
|
||||
struct amount_msat local_channel_capacity(const struct local_channel *lc);
|
||||
|
||||
/* Check local channel matches these */
|
||||
bool layer_check_local_channel(const struct local_channel *lc,
|
||||
const struct node_id *n1,
|
||||
const struct node_id *n2,
|
||||
struct amount_msat capacity);
|
||||
|
||||
/* Update a local channel to a layer: fails if you try to change capacity or nodes! */
|
||||
void layer_update_local_channel(struct layer *layer,
|
||||
const struct node_id *src,
|
||||
const struct node_id *dst,
|
||||
struct short_channel_id scid,
|
||||
struct amount_msat capacity,
|
||||
struct amount_msat base_fee,
|
||||
u32 proportional_fee,
|
||||
u16 delay,
|
||||
struct amount_msat htlc_min,
|
||||
struct amount_msat htlc_max);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Add/update a constraint 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);
|
||||
|
||||
/* Remove constraints older then cutoff: returns num removed. */
|
||||
size_t layer_trim_constraints(struct layer *layer, u64 cutoff);
|
||||
|
||||
/* Add a disabled node to a layer. */
|
||||
void layer_add_disabled_node(struct layer *layer, const struct node_id *node);
|
||||
|
||||
/* Print out a json object per layer, or all if layer is NULL */
|
||||
void json_add_layers(struct json_stream *js,
|
||||
struct askrene *askrene,
|
||||
const char *fieldname,
|
||||
const char *layername);
|
||||
|
||||
/* Print a single constraint */
|
||||
void json_add_constraint(struct json_stream *js,
|
||||
const char *fieldname,
|
||||
const struct constraint *c,
|
||||
const struct layer *layer);
|
||||
|
||||
/* Scan for memleaks */
|
||||
void layer_memleak_mark(struct askrene *askrene, struct htable *memtable);
|
||||
#endif /* LIGHTNING_PLUGINS_ASKRENE_LAYER_H */
|
Loading…
Add table
Reference in a new issue