diff --git a/contrib/pylightning/lightning/lightning.py b/contrib/pylightning/lightning/lightning.py index 5b20b3cd7..94e4ea08e 100644 --- a/contrib/pylightning/lightning/lightning.py +++ b/contrib/pylightning/lightning/lightning.py @@ -596,8 +596,8 @@ class LightningRpc(UnixDomainSocketRpc): {cltv} (default 9). If specified search from {fromid} otherwise use this node as source. Randomize the route with up to {fuzzpercent} (0.0 -> 100.0, default 5.0). {exclude} is an optional array of - scid/direction to exclude. Limit the number of hops in the route to - {maxhops}. + scid/direction or node-id to exclude. Limit the number of hops in the + route to {maxhops}. """ payload = { "id": node_id, diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index b9c988fb8..0e300e18d 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -39,7 +39,7 @@ msgdata,gossip_getroute_request,riskfactor_by_million,u64, msgdata,gossip_getroute_request,final_cltv,u32, msgdata,gossip_getroute_request,fuzz,double, msgdata,gossip_getroute_request,num_excluded,u16, -msgdata,gossip_getroute_request,excluded,short_channel_id_dir,num_excluded +msgdata,gossip_getroute_request,excluded,exclude_entry,num_excluded msgdata,gossip_getroute_request,max_hops,u32, msgtype,gossip_getroute_reply,3106 diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 81d0ea5f3..02e920576 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -2488,7 +2488,7 @@ static struct io_plan *getroute_req(struct io_conn *conn, struct daemon *daemon, u8 *out; struct route_hop *hops; double fuzz; - struct short_channel_id_dir *excluded; + struct exclude_entry **excluded; /* To choose between variations, we need to know how much we're * sending (eliminates too-small channels, and also effects the fees diff --git a/gossipd/routing.c b/gossipd/routing.c index fea9e507e..43ffca6d3 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -2336,7 +2336,7 @@ struct route_hop *get_route(const tal_t *ctx, struct routing_state *rstate, struct amount_msat msat, double riskfactor, u32 final_cltv, double fuzz, u64 seed, - const struct short_channel_id_dir *excluded, + struct exclude_entry **excluded, size_t max_hops) { struct chan **route; @@ -2346,22 +2346,49 @@ struct route_hop *get_route(const tal_t *ctx, struct routing_state *rstate, struct route_hop *hops; struct node *n; struct amount_msat *saved_capacity; + struct short_channel_id_dir *excluded_chan; struct siphash_seed base_seed; - saved_capacity = tal_arr(tmpctx, struct amount_msat, tal_count(excluded)); + saved_capacity = tal_arr(tmpctx, struct amount_msat, 0); + excluded_chan = tal_arr(tmpctx, struct short_channel_id_dir, 0); base_seed.u.u64[0] = base_seed.u.u64[1] = seed; if (amount_msat_eq(msat, AMOUNT_MSAT(0))) return NULL; - /* Temporarily set excluded channels' capacity to zero. */ + /* Temporarily set the capacity of the excluded channels and the incoming channels + * of excluded nodes to zero. */ for (size_t i = 0; i < tal_count(excluded); i++) { - struct chan *chan = get_channel(rstate, &excluded[i].scid); - if (!chan) - continue; - saved_capacity[i] = chan->half[excluded[i].dir].htlc_maximum; - chan->half[excluded[i].dir].htlc_maximum = AMOUNT_MSAT(0); + if (excluded[i]->type == EXCLUDE_CHANNEL) { + struct short_channel_id_dir *chan_id = &excluded[i]->u.chan_id; + struct chan *chan = get_channel(rstate, &chan_id->scid); + if (!chan) + continue; + tal_arr_expand(&saved_capacity, chan->half[chan_id->dir].htlc_maximum); + tal_arr_expand(&excluded_chan, *chan_id); + chan->half[chan_id->dir].htlc_maximum = AMOUNT_MSAT(0); + } else { + assert(excluded[i]->type == EXCLUDE_NODE); + + struct node *node = get_node(rstate, &excluded[i]->u.node_id); + if (!node) + continue; + + struct chan_map_iter i; + struct chan *chan; + for (chan = first_chan(node, &i); chan; chan = next_chan(node, &i)) { + int dir = half_chan_to(node, chan); + tal_arr_expand(&saved_capacity, chan->half[dir].htlc_maximum); + + struct short_channel_id_dir id; + id.scid = chan->scid; + id.dir = dir; + tal_arr_expand(&excluded_chan, id); + + chan->half[dir].htlc_maximum = AMOUNT_MSAT(0); + } + } } route = find_route(ctx, rstate, source, destination, msat, @@ -2378,11 +2405,11 @@ struct route_hop *get_route(const tal_t *ctx, struct routing_state *rstate, * By restoring in reverse order we ensure we can restore * the correct capacity. */ - for (ssize_t i = tal_count(excluded) - 1; i >= 0; i--) { - struct chan *chan = get_channel(rstate, &excluded[i].scid); + for (ssize_t i = tal_count(excluded_chan) - 1; i >= 0; i--) { + struct chan *chan = get_channel(rstate, &excluded_chan[i].scid); if (!chan) continue; - chan->half[excluded[i].dir].htlc_maximum = saved_capacity[i]; + chan->half[excluded_chan[i].dir].htlc_maximum = saved_capacity[i]; } if (!route) { diff --git a/gossipd/routing.h b/gossipd/routing.h index a2555f34b..c0e01e80e 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -348,7 +348,7 @@ struct route_hop *get_route(const tal_t *ctx, struct routing_state *rstate, u32 final_cltv, double fuzz, u64 seed, - const struct short_channel_id_dir *excluded, + struct exclude_entry **excluded, size_t max_hops); /* Disable channel(s) based on the given routing failure. */ void routing_failure(struct routing_state *rstate, diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c index f84f4b90f..07bd7ec37 100644 --- a/gossipd/test/run-crc32_of_update.c +++ b/gossipd/test/run-crc32_of_update.c @@ -87,7 +87,7 @@ bool fromwire_gossip_get_incoming_channels(const tal_t *ctx UNNEEDED, const void bool fromwire_gossip_getnodes_request(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id **id UNNEEDED) { fprintf(stderr, "fromwire_gossip_getnodes_request called!\n"); abort(); } /* Generated stub for fromwire_gossip_getroute_request */ -bool fromwire_gossip_getroute_request(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id **source UNNEEDED, struct node_id *destination UNNEEDED, struct amount_msat *msatoshi UNNEEDED, u64 *riskfactor_by_million UNNEEDED, u32 *final_cltv UNNEEDED, double *fuzz UNNEEDED, struct short_channel_id_dir **excluded UNNEEDED, u32 *max_hops UNNEEDED) +bool fromwire_gossip_getroute_request(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id **source UNNEEDED, struct node_id *destination UNNEEDED, struct amount_msat *msatoshi UNNEEDED, u64 *riskfactor_by_million UNNEEDED, u32 *final_cltv UNNEEDED, double *fuzz UNNEEDED, struct exclude_entry ***excluded UNNEEDED, u32 *max_hops UNNEEDED) { fprintf(stderr, "fromwire_gossip_getroute_request called!\n"); abort(); } /* Generated stub for fromwire_gossip_get_txout_reply */ bool fromwire_gossip_get_txout_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct amount_sat *satoshis UNNEEDED, u8 **outscript UNNEEDED) @@ -140,7 +140,7 @@ struct route_hop *get_route(const tal_t *ctx UNNEEDED, struct routing_state *rst u32 final_cltv UNNEEDED, double fuzz UNNEEDED, u64 seed UNNEEDED, - const struct short_channel_id_dir *excluded UNNEEDED, + struct exclude_entry **excluded UNNEEDED, size_t max_hops UNNEEDED) { fprintf(stderr, "get_route called!\n"); abort(); } /* Generated stub for gossip_peerd_wire_type_name */ diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c index df9b9eaa2..e06fb3dd1 100644 --- a/gossipd/test/run-extended-info.c +++ b/gossipd/test/run-extended-info.c @@ -110,7 +110,7 @@ bool fromwire_gossip_get_incoming_channels(const tal_t *ctx UNNEEDED, const void bool fromwire_gossip_getnodes_request(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id **id UNNEEDED) { fprintf(stderr, "fromwire_gossip_getnodes_request called!\n"); abort(); } /* Generated stub for fromwire_gossip_getroute_request */ -bool fromwire_gossip_getroute_request(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id **source UNNEEDED, struct node_id *destination UNNEEDED, struct amount_msat *msatoshi UNNEEDED, u64 *riskfactor_by_million UNNEEDED, u32 *final_cltv UNNEEDED, double *fuzz UNNEEDED, struct short_channel_id_dir **excluded UNNEEDED, u32 *max_hops UNNEEDED) +bool fromwire_gossip_getroute_request(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id **source UNNEEDED, struct node_id *destination UNNEEDED, struct amount_msat *msatoshi UNNEEDED, u64 *riskfactor_by_million UNNEEDED, u32 *final_cltv UNNEEDED, double *fuzz UNNEEDED, struct exclude_entry ***excluded UNNEEDED, u32 *max_hops UNNEEDED) { fprintf(stderr, "fromwire_gossip_getroute_request called!\n"); abort(); } /* Generated stub for fromwire_gossip_get_txout_reply */ bool fromwire_gossip_get_txout_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct amount_sat *satoshis UNNEEDED, u8 **outscript UNNEEDED) @@ -163,7 +163,7 @@ struct route_hop *get_route(const tal_t *ctx UNNEEDED, struct routing_state *rst u32 final_cltv UNNEEDED, double fuzz UNNEEDED, u64 seed UNNEEDED, - const struct short_channel_id_dir *excluded UNNEEDED, + struct exclude_entry **excluded UNNEEDED, size_t max_hops UNNEEDED) { fprintf(stderr, "get_route called!\n"); abort(); } /* Generated stub for gossip_peerd_wire_type_name */ diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index a2124f0d2..6da7b3c29 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -313,7 +313,7 @@ static struct command_result *json_getroute(struct command *cmd, struct amount_msat *msat; unsigned *cltv; double *riskfactor; - struct short_channel_id_dir *excluded; + const struct exclude_entry **excluded; u32 *max_hops; /* Higher fuzz means that some high-fee paths can be discounted @@ -343,19 +343,31 @@ static struct command_result *json_getroute(struct command *cmd, const jsmntok_t *t; size_t i; - excluded = tal_arr(cmd, struct short_channel_id_dir, - excludetok->size); + 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, - &excluded[i])) { - return command_fail(cmd, JSONRPC2_INVALID_PARAMS, - "%.*s is not a valid" - " short_channel_id/direction", - t->end - t->start, - buffer + 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(cmd, JSONRPC2_INVALID_PARAMS, + "%.*s is not a valid" + " short_channel_id/node_id", + t->end - t->start, + buffer + t->start); + + 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; @@ -379,7 +391,7 @@ static const struct json_command getroute_command = { "If specified search from {fromid} otherwise use this node as source. " "Randomize the route with up to {fuzzpercent} (default 5.0). " "{exclude} an array of short-channel-id/direction (e.g. [ '564334x877x1/0', '564195x1292x0/1' ]) " - "from consideration. " + "or node-id from consideration. " "Set the {maxhops} the route can take (default 20)." }; AUTODATA(json_command, &getroute_command);