diff --git a/gossipd/gossip_wire.csv b/gossipd/gossip_wire.csv index b6641c195..56fb5b5e1 100644 --- a/gossipd/gossip_wire.csv +++ b/gossipd/gossip_wire.csv @@ -32,6 +32,9 @@ gossip_getroute_request,,msatoshi,u64 gossip_getroute_request,,riskfactor,u16 gossip_getroute_request,,final_cltv,u32 gossip_getroute_request,,fuzz,double +gossip_getroute_request,,num_excluded,u16 +gossip_getroute_request,,excluded,num_excluded*struct short_channel_id +gossip_getroute_request,,excluded_dir,num_excluded*bool gossip_getroute_reply,3106 gossip_getroute_reply,,num_hops,u16 diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index a00b3868b..8d7c80a44 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -1870,6 +1870,8 @@ 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 *excluded; + bool *excluded_dir; /* To choose between variations, we need to know how much we're * sending (eliminates too-small channels, and also effects the fees @@ -1877,10 +1879,11 @@ static struct io_plan *getroute_req(struct io_conn *conn, struct daemon *daemon, * much cltv we need a the final node to give exact values for each * intermediate hop, as well as how much random fuzz to inject to * avoid being too predictable. */ - if (!fromwire_gossip_getroute_request(msg, + if (!fromwire_gossip_getroute_request(msg, msg, &source, &destination, &msatoshi, &riskfactor, - &final_cltv, &fuzz)) + &final_cltv, &fuzz, + &excluded, &excluded_dir)) master_badmsg(WIRE_GOSSIP_GETROUTE_REQUEST, msg); status_trace("Trying to find a route from %s to %s for %"PRIu64" msatoshi", @@ -1890,7 +1893,7 @@ static struct io_plan *getroute_req(struct io_conn *conn, struct daemon *daemon, /* routing.c does all the hard work; can return NULL. */ hops = get_route(tmpctx, daemon->rstate, &source, &destination, msatoshi, riskfactor, final_cltv, - fuzz, siphash_seed()); + fuzz, siphash_seed(), excluded, excluded_dir); out = towire_gossip_getroute_reply(NULL, hops); daemon_conn_send(daemon->master, take(out)); diff --git a/gossipd/routing.c b/gossipd/routing.c index 217ebe6dc..781d5c543 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -1496,20 +1496,44 @@ struct route_hop *get_route(const tal_t *ctx, struct routing_state *rstate, const struct pubkey *destination, const u64 msatoshi, double riskfactor, u32 final_cltv, - double fuzz, const struct siphash_seed *base_seed) + double fuzz, const struct siphash_seed *base_seed, + const struct short_channel_id *excluded, + const bool *excluded_dir) { struct chan **route; u64 total_amount; unsigned int total_delay; u64 fee; struct route_hop *hops; - int i; struct node *n; + u64 *saved_capacity; + + assert(tal_count(excluded) == tal_count(excluded_dir)); + saved_capacity = tal_arr(tmpctx, u64, tal_count(excluded)); + + /* Temporarily set excluded channels' capacity to zero. */ + for (size_t i = 0; i < tal_count(excluded); i++) { + struct chan *chan = get_channel(rstate, &excluded[i]); + if (!chan) + continue; + saved_capacity[i] + = chan->half[excluded_dir[i]].htlc_maximum_msat; + chan->half[excluded_dir[i]].htlc_maximum_msat = 0; + } route = find_route(ctx, rstate, source, destination, msatoshi, riskfactor / BLOCKS_PER_YEAR / 10000, fuzz, base_seed, &fee); + /* Now restore the capacity. */ + for (size_t i = 0; i < tal_count(excluded); i++) { + struct chan *chan = get_channel(rstate, &excluded[i]); + if (!chan) + continue; + chan->half[excluded_dir[i]].htlc_maximum_msat + = saved_capacity[i]; + } + if (!route) { return NULL; } @@ -1521,7 +1545,7 @@ struct route_hop *get_route(const tal_t *ctx, struct routing_state *rstate, /* Start at destination node. */ n = get_node(rstate, destination); - for (i = tal_count(route) - 1; i >= 0; i--) { + for (int i = tal_count(route) - 1; i >= 0; i--) { const struct half_chan *c; int idx = half_chan_to(n, route[i]); c = &route[i]->half[idx]; diff --git a/gossipd/routing.h b/gossipd/routing.h index 71bc565e2..930c6d44e 100644 --- a/gossipd/routing.h +++ b/gossipd/routing.h @@ -267,7 +267,9 @@ struct route_hop *get_route(const tal_t *ctx, struct routing_state *rstate, const u64 msatoshi, double riskfactor, u32 final_cltv, double fuzz, - const struct siphash_seed *base_seed); + const struct siphash_seed *base_seed, + const struct short_channel_id *excluded, + const bool *excluded_dir); /* Disable channel(s) based on the given routing failure. */ void routing_failure(struct routing_state *rstate, const struct pubkey *erring_node, diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 3ceb59143..1a94ff08d 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -325,7 +325,8 @@ static struct command_result *json_getroute(struct command *cmd, u8 *req = towire_gossip_getroute_request(cmd, source, destination, *msatoshi, *riskfactor * 1000, - *cltv, fuzz); + *cltv, fuzz, + NULL, NULL); subd_req(ld->gossip, ld->gossip, req, -1, 0, json_getroute_reply, cmd); return command_still_pending(cmd); } diff --git a/lightningd/payalgo.c b/lightningd/payalgo.c index ac943afec..0295f893d 100644 --- a/lightningd/payalgo.c +++ b/lightningd/payalgo.c @@ -557,7 +557,7 @@ static struct command_result *json_pay_try(struct pay *pay) pay->msatoshi + overpayment, pay->riskfactor, pay->min_final_cltv_expiry, - &pay->fuzz); + &pay->fuzz, NULL, NULL); subd_req(pay->try_parent, cmd->ld->gossip, req, -1, 0, json_pay_getroute_reply, pay); return NULL;