diff --git a/common/json_tok.c b/common/json_tok.c index 33e4b7d90..ad159cfd1 100644 --- a/common/json_tok.c +++ b/common/json_tok.c @@ -10,6 +10,7 @@ #include #include #include +#include struct command_result *param_array(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, @@ -649,6 +650,64 @@ param_routehint_array(struct command *cmd, const char *name, const char *buffer, return NULL; } +struct command_result *param_route_exclusion(struct command *cmd, + const char *name, const char *buffer, const jsmntok_t *tok, + struct route_exclusion **re) +{ + *re = tal(cmd, struct route_exclusion); + struct short_channel_id_dir *chan_id = + tal(tmpctx, struct short_channel_id_dir); + if (!short_channel_id_dir_from_str(buffer + tok->start, + tok->end - tok->start, + chan_id)) { + struct node_id *node_id = tal(tmpctx, struct node_id); + + if (!json_to_node_id(buffer, tok, node_id)) + return command_fail_badparam(cmd, "exclude", + buffer, tok, + "should be short_channel_id_dir or node_id"); + + (*re)->type = EXCLUDE_NODE; + (*re)->u.node_id = *node_id; + } else { + (*re)->type = EXCLUDE_CHANNEL; + (*re)->u.chan_id = *chan_id; + } + + return NULL; +} + +struct command_result * +param_route_exclusion_array(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct route_exclusion ***res) +{ + size_t i; + const jsmntok_t *curr; + char *element_name; + struct command_result *err; + if (tok->type != JSMN_ARRAY) { + return command_fail( + cmd, JSONRPC2_INVALID_PARAMS, + "Exclude array %s (\"%s\") is not an array", + name, json_strdup(tmpctx, buffer, tok)); + } + + *res = tal_arr(cmd, struct route_exclusion *, 0); + json_for_each_arr(i, curr, tok) { + struct route_exclusion *element; + element_name = tal_fmt(cmd, "%s[%zu]", name, i); + err = param_route_exclusion(cmd, element_name, buffer, curr, &element); + if (err != NULL) { + return err; + } + tal_arr_expand(res, element); + + tal_free(element_name); + } + return NULL; +} + struct command_result *param_lease_hex(struct command *cmd, const char *name, const char *buffer, diff --git a/common/json_tok.h b/common/json_tok.h index 74dbf58a6..cbaa61dfa 100644 --- a/common/json_tok.h +++ b/common/json_tok.h @@ -18,6 +18,7 @@ struct channel_id; struct command; struct command_result; struct json_escape; +struct route_exclusion; struct sha256; struct wally_psbt; @@ -205,6 +206,15 @@ struct command_result * param_routehint_array(struct command *cmd, const char *name, const char *buffer, const jsmntok_t *tok, struct route_info ***ris); +struct command_result *param_route_exclusion(struct command *cmd, + const char *name, const char *buffer, const jsmntok_t *tok, + struct route_exclusion **re); + +struct command_result * +param_route_exclusion_array(struct command *cmd, const char *name, + const char *buffer, const jsmntok_t *tok, + struct route_exclusion ***res); + /** * Parse a 'compact-lease' (serialized lease_rates) back into lease_rates */ diff --git a/common/route.h b/common/route.h index 1fc474234..11963fdab 100644 --- a/common/route.h +++ b/common/route.h @@ -74,4 +74,22 @@ struct route_hop *route_from_dijkstra(const tal_t *ctx, const struct gossmap_node *src, struct amount_msat final_amount, u32 final_cltv); + +/* + * Manually exlude nodes or channels from a route. + * Used with `getroute` and `pay` commands + */ +enum route_exclusion_type { + EXCLUDE_CHANNEL = 1, + EXCLUDE_NODE = 2 +}; + +struct route_exclusion { + enum route_exclusion_type type; + union { + struct short_channel_id_dir chan_id; + struct node_id node_id; + } u; +}; + #endif /* LIGHTNING_COMMON_ROUTE_H */ diff --git a/plugins/topology.c b/plugins/topology.c index 3d30582d6..cfc3bd8ea 100644 --- a/plugins/topology.c +++ b/plugins/topology.c @@ -30,19 +30,6 @@ static struct gossmap *get_gossmap(void) /* Convenience global since route_score_fuzz doesn't take args. 0 to 1. */ static double fuzz; -enum exclude_entry_type { - EXCLUDE_CHANNEL = 1, - EXCLUDE_NODE = 2 -}; - -struct exclude_entry { - enum exclude_entry_type type; - union { - struct short_channel_id_dir chan_id; - struct node_id node_id; - } u; -}; - /* Prioritize costs over distance, but with fuzz. Cost must be * the same when the same channel queried, so we base it on that. */ static u64 route_score_fuzz(u32 distance, @@ -68,7 +55,7 @@ static bool can_carry(const struct gossmap *map, const struct gossmap_chan *c, int dir, struct amount_msat amount, - const struct exclude_entry **excludes) + struct route_exclusion **excludes) { struct node_id dstid; @@ -143,12 +130,11 @@ static struct command_result *json_getroute(struct command *cmd, { struct node_id *destination; struct node_id *source; - const jsmntok_t *excludetok; struct amount_msat *msat; u32 *cltv; /* risk factor 12.345% -> riskfactor_millionths = 12345000 */ u64 *riskfactor_millionths, *fuzz_millionths; - const struct exclude_entry **excluded; + struct route_exclusion **excluded; u32 *max_hops; const struct dijkstra *dij; struct route_hop *route; @@ -164,7 +150,7 @@ static struct command_result *json_getroute(struct command *cmd, p_opt_def("fromid", param_node_id, &source, local_id), p_opt_def("fuzzpercent", param_millionths, &fuzz_millionths, 5000000), - p_opt("exclude", param_array, &excludetok), + p_opt("exclude", param_route_exclusion_array, &excluded), p_opt_def("maxhops", param_number, &max_hops, ROUTING_MAX_HOPS), NULL)) return command_param_failed(); @@ -176,38 +162,6 @@ static struct command_result *json_getroute(struct command *cmd, buffer, params, "should be <= 100"); - if (excludetok) { - const jsmntok_t *t; - size_t i; - - excluded = tal_arr(cmd, const struct exclude_entry *, 0); - - json_for_each_arr(i, t, excludetok) { - struct exclude_entry *entry = tal(excluded, struct exclude_entry); - struct short_channel_id_dir *chan_id = tal(tmpctx, struct short_channel_id_dir); - if (!short_channel_id_dir_from_str(buffer + t->start, - t->end - t->start, - chan_id)) { - struct node_id *node_id = tal(tmpctx, struct node_id); - - if (!json_to_node_id(buffer, t, node_id)) - return command_fail_badparam(cmd, "exclude", - buffer, t, - "should be short_channel_id or node_id"); - - entry->type = EXCLUDE_NODE; - entry->u.node_id = *node_id; - } else { - entry->type = EXCLUDE_CHANNEL; - entry->u.chan_id = *chan_id; - } - - tal_arr_expand(&excluded, entry); - } - } else { - excluded = NULL; - } - gossmap = get_gossmap(); src = gossmap_find_node(gossmap, source); if (!src)