From cdaad1bf2ab8fa204eadddb04394ee88342d5508 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 7 Dec 2023 06:44:05 +1030 Subject: [PATCH] common: generic routine to turn listpeerchannels into gossmap local updates. This is more thorough than the minimal one required for getroute(), including the feerates and cltv deltas. Signed-off-by: Rusty Russell --- common/Makefile | 1 + common/gossmods_listpeerchannels.c | 158 +++++++++++++++++++++++++++++ common/gossmods_listpeerchannels.h | 74 ++++++++++++++ plugins/topology.c | 88 +--------------- 4 files changed, 238 insertions(+), 83 deletions(-) create mode 100644 common/gossmods_listpeerchannels.c create mode 100644 common/gossmods_listpeerchannels.h diff --git a/common/Makefile b/common/Makefile index 3e6a5e167..d37acb4c9 100644 --- a/common/Makefile +++ b/common/Makefile @@ -39,6 +39,7 @@ COMMON_SRC_NOGEN := \ common/fp16.c \ common/gossip_store.c \ common/gossmap.c \ + common/gossmods_listpeerchannels.c \ common/hash_u5.c \ common/hmac.c \ common/hsm_encryption.c \ diff --git a/common/gossmods_listpeerchannels.c b/common/gossmods_listpeerchannels.c new file mode 100644 index 000000000..297e28a25 --- /dev/null +++ b/common/gossmods_listpeerchannels.c @@ -0,0 +1,158 @@ +#include "config.h" +#include +#include +#include +#include +#include + +void gossmod_add_localchan(struct gossmap_localmods *mods, + const struct node_id *self, + const struct node_id *peer, + const struct short_channel_id_dir *scidd, + struct amount_msat min, + struct amount_msat max, + struct amount_msat fee_base, + u32 fee_proportional, + u32 cltv_delta, + bool enabled, + const char *buf UNUSED, + const jsmntok_t *chantok UNUSED, + void *cbarg UNUSED) +{ + /* FIXME: features? */ + gossmap_local_addchan(mods, self, peer, &scidd->scid, NULL); + + gossmap_local_updatechan(mods, &scidd->scid, min, max, + fee_base.millisatoshis, /* Raw: gossmap */ + fee_proportional, + cltv_delta, + enabled, + scidd->dir); +} + +struct gossmap_localmods * +gossmods_from_listpeerchannels_(const tal_t *ctx, + const struct node_id *self, + const char *buf, + const jsmntok_t *toks, + void (*cb)(struct gossmap_localmods *mods, + const struct node_id *self, + const struct node_id *peer, + const struct short_channel_id_dir *scidd, + struct amount_msat min, + struct amount_msat max, + struct amount_msat fee_base, + u32 fee_proportional, + u32 cltv_delta, + bool enabled, + const char *buf, + const jsmntok_t *chantok, + void *cbarg), + void *cbarg) +{ + struct gossmap_localmods *mods = gossmap_localmods_new(ctx); + const jsmntok_t *channels, *channel; + size_t i; + + channels = json_get_member(buf, toks, "channels"); + json_for_each_arr(i, channel, channels) { + struct short_channel_id_dir scidd; + struct short_channel_id alias; + bool enabled; + struct node_id dst; + struct amount_msat spendable, receivable, fee_base[NUM_SIDES], htlc_min[NUM_SIDES], htlc_max[NUM_SIDES]; + u32 fee_proportional[NUM_SIDES], cltv_delta[NUM_SIDES]; + const char *state, *err; + + /* scid/direction and alias may not exist. */ + scidd.scid.u64 = 0; + alias.u64 = 0; + + /* We do this to note if we have no remote update. */ + fee_proportional[REMOTE] = -1U; + + err = json_scan(tmpctx, buf, channel, + "{short_channel_id?:%," + "direction?:%," + "spendable_msat?:%," + "receivable_msat?:%," + "peer_connected:%," + "state:%," + "peer_id:%," + "updates?:{" + "local" + ":{fee_base_msat:%," + "fee_proportional_millionths:%," + "htlc_minimum_msat:%," + "htlc_maximum_msat:%," + "cltv_expiry_delta:%}," + "remote?" + ":{fee_base_msat:%," + "fee_proportional_millionths:%," + "htlc_minimum_msat:%," + "htlc_maximum_msat:%," + "cltv_expiry_delta:%}}," + "alias?:{local:%}}", + JSON_SCAN(json_to_short_channel_id, &scidd.scid), + JSON_SCAN(json_to_int, &scidd.dir), + JSON_SCAN(json_to_msat, &spendable), + JSON_SCAN(json_to_msat, &receivable), + JSON_SCAN(json_to_bool, &enabled), + JSON_SCAN_TAL(tmpctx, json_strdup, &state), + JSON_SCAN(json_to_node_id, &dst), + JSON_SCAN(json_to_msat, &fee_base[LOCAL]), + JSON_SCAN(json_to_u32, &fee_proportional[LOCAL]), + JSON_SCAN(json_to_msat, &htlc_min[LOCAL]), + JSON_SCAN(json_to_msat, &htlc_max[LOCAL]), + JSON_SCAN(json_to_u32, &cltv_delta[LOCAL]), + JSON_SCAN(json_to_msat, &fee_base[REMOTE]), + JSON_SCAN(json_to_u32, &fee_proportional[REMOTE]), + JSON_SCAN(json_to_msat, &htlc_min[REMOTE]), + JSON_SCAN(json_to_msat, &htlc_max[REMOTE]), + JSON_SCAN(json_to_u32, &cltv_delta[REMOTE]), + JSON_SCAN(json_to_short_channel_id, &alias)); + if (err) { + errx(1, "Bad listpeerchannels.channels %zu: %s", + i, err); + } + + /* Use alias if no scid. Note: if alias is set, direction is present */ + if (scidd.scid.u64 == 0 && alias.u64 != 0) + scidd.scid = alias; + + /* Unusable if no scid (yet) */ + if (scidd.scid.u64 == 0) + continue; + + /* Disable if in bad state (it's already false if not connected) */ + if (!streq(state, "CHANNELD_NORMAL") + && !streq(state, "CHANNELD_AWAITING_SPLICE")) + enabled = false; + + /* Cut htlc max to spendable. */ + if (amount_msat_less(spendable, htlc_max[LOCAL])) + htlc_max[LOCAL] = spendable; + + /* We add both directions */ + cb(mods, self, &dst, &scidd, htlc_min[LOCAL], htlc_max[LOCAL], + fee_base[LOCAL], fee_proportional[LOCAL], cltv_delta[LOCAL], + enabled, buf, channel, cbarg); + + /* If we didn't have a remote update, it's not usable yet */ + if (fee_proportional[REMOTE] == -1U) + continue; + + scidd.dir = !scidd.dir; + + /* Cut htlc max to receivable. */ + if (amount_msat_less(receivable, htlc_max[REMOTE])) + htlc_max[REMOTE] = receivable; + + cb(mods, self, &dst, &scidd, htlc_min[REMOTE], htlc_max[REMOTE], + fee_base[REMOTE], fee_proportional[REMOTE], cltv_delta[REMOTE], + enabled, buf, channel, cbarg); + } + + return mods; +} + diff --git a/common/gossmods_listpeerchannels.h b/common/gossmods_listpeerchannels.h new file mode 100644 index 000000000..f60d125e9 --- /dev/null +++ b/common/gossmods_listpeerchannels.h @@ -0,0 +1,74 @@ +#ifndef LIGHTNING_COMMON_GOSSMODS_LISTPEERCHANNELS_H +#define LIGHTNING_COMMON_GOSSMODS_LISTPEERCHANNELS_H +#include "config.h" +#include +#include +#include +#include + +struct node_id; + +/** + * gossmods_from_listpeerchannels: create gossmap_localmods from `listpeerchannels` + * @ctx: context to allocate return from + * @buf: the JSON buffer from listpeerchannels + * @toks: the JSON tokens + * @cb: optional per-channel callback. + * @cbarg: arg for @cb. + * + * This constructs a set of modifications you can apply to your gossmap to include + * local (esp. private) channels. You can also have an optional per-channel callback + * for special effects. + */ +struct gossmap_localmods *gossmods_from_listpeerchannels_(const tal_t *ctx, + const struct node_id *self, + const char *buf, + const jsmntok_t *toks, + void (*cb)(struct gossmap_localmods *mods, + const struct node_id *self_, + const struct node_id *peer, + const struct short_channel_id_dir *scidd, + struct amount_msat min, + struct amount_msat max, + struct amount_msat fee_base, + u32 fee_proportional, + u32 cltv_delta, + bool enabled, + const char *buf_, + const jsmntok_t *chantok, + void *cbarg_), + void *cbarg); + +#define gossmods_from_listpeerchannels(ctx, self, buf, toks, cb, cbarg) \ + gossmods_from_listpeerchannels_((ctx), (self), (buf), (toks), \ + typesafe_cb_preargs(void, void *, (cb), (cbarg), \ + struct gossmap_localmods *, \ + const struct node_id *, \ + const struct node_id *, \ + const struct short_channel_id_dir *, \ + struct amount_msat, \ + struct amount_msat, \ + struct amount_msat, \ + u32, \ + u32, \ + bool, \ + const char *, \ + const jsmntok_t *), \ + (cbarg)) + +/* Callback which simply adds to gossmap. */ +void gossmod_add_localchan(struct gossmap_localmods *mods, + const struct node_id *self, + const struct node_id *peer, + const struct short_channel_id_dir *scidd, + struct amount_msat min, + struct amount_msat max, + struct amount_msat fee_base, + u32 fee_proportional, + u32 cltv_delta, + bool enabled, + const char *buf UNUSED, + const jsmntok_t *chantok UNUSED, + void *cbarg UNUSED); + +#endif /* LIGHTNING_COMMON_GOSSMODS_LISTPEERCHANNELS_H */ diff --git a/plugins/topology.c b/plugins/topology.c index 296c61574..b7d24a9bd 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -148,86 +149,6 @@ static struct command_result *try_route(struct command *cmd, return command_finished(cmd, js); } -static struct gossmap_localmods * -gossmods_from_listpeerchannels(const tal_t *ctx, - struct plugin *plugin, - struct gossmap *gossmap, - const char *buf, - const jsmntok_t *toks) -{ - struct gossmap_localmods *mods = gossmap_localmods_new(ctx); - const jsmntok_t *channels, *channel; - size_t i; - - channels = json_get_member(buf, toks, "channels"); - json_for_each_arr(i, channel, channels) { - struct short_channel_id scid; - int dir; - bool connected; - struct node_id dst; - struct amount_msat capacity; - const char *state, *err; - - /* scid/direction may not exist. */ - scid.u64 = 0; - capacity = AMOUNT_MSAT(0); - err = json_scan(tmpctx, buf, channel, - "{short_channel_id?:%," - "direction?:%," - "spendable_msat?:%," - "peer_connected:%," - "state:%," - "peer_id:%}", - JSON_SCAN(json_to_short_channel_id, &scid), - JSON_SCAN(json_to_int, &dir), - JSON_SCAN(json_to_msat, &capacity), - JSON_SCAN(json_to_bool, &connected), - JSON_SCAN_TAL(tmpctx, json_strdup, &state), - JSON_SCAN(json_to_node_id, &dst)); - if (err) { - plugin_err(plugin, - "Bad listpeerchannels.channels %zu: %s", - i, err); - } - - /* Unusable if no scid (yet) */ - if (scid.u64 == 0) - continue; - - /* Disable if in bad state, or disconnected */ - if (!streq(state, "CHANNELD_NORMAL") - && !streq(state, "CHANNELD_AWAITING_SPLICE")) { - goto disable; - } - - if (!connected) { - goto disable; - } - - /* FIXME: features? */ - gossmap_local_addchan(mods, &local_id, &dst, &scid, NULL); - gossmap_local_updatechan(mods, &scid, - AMOUNT_MSAT(0), capacity, - /* We don't charge ourselves fees */ - 0, 0, 0, - true, - dir); - continue; - - disable: - /* Only apply fake "disabled" if channel exists */ - if (gossmap_find_chan(gossmap, &scid)) { - gossmap_local_updatechan(mods, &scid, - AMOUNT_MSAT(0), AMOUNT_MSAT(0), - 0, 0, 0, - false, - dir); - } - } - - return mods; -} - static struct command_result * listpeerchannels_getroute_done(struct command *cmd, const char *buf, @@ -239,11 +160,12 @@ listpeerchannels_getroute_done(struct command *cmd, struct command_result *res; /* Get local knowledge */ - gossmap = get_gossmap(); - mods = gossmods_from_listpeerchannels(tmpctx, cmd->plugin, - gossmap, buf, result); + mods = gossmods_from_listpeerchannels(tmpctx, &local_id, + buf, result, + gossmod_add_localchan, NULL); /* Overlay local knowledge for dijkstra */ + gossmap = get_gossmap(); gossmap_apply_localmods(gossmap, mods); res = try_route(cmd, gossmap, info); gossmap_remove_localmods(gossmap, mods);