topology: in deterministic mode, only return one best candidate for listincoming.

This ensures that bolt11/bolt12 selection of routehints/blinded paths is always the same.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2025-11-13 16:02:39 +10:30
parent 8c2116a123
commit acbfaa00f4

View File

@@ -8,6 +8,7 @@
#include <common/json_param.h>
#include <common/json_stream.h>
#include <common/memleak.h>
#include <common/randbytes.h>
#include <common/route.h>
#include <errno.h>
#include <inttypes.h>
@@ -459,6 +460,33 @@ static struct amount_msat peer_capacity(const struct gossmap *gossmap,
return capacity;
}
/* For deterministic results with bolt12/11 routes, we only return a
* single candidate: choose the one with most capacity */
static size_t best_candidate(const struct gossmap *gossmap,
const struct gossmap_node *me)
{
struct amount_msat best_cap = AMOUNT_MSAT(0);
size_t best_num = 0;
for (size_t i = 0; i < me->num_chans; i++) {
int dir;
struct gossmap_chan *ourchan;
struct amount_msat cap;
struct gossmap_node *peer;
ourchan = gossmap_nth_chan(gossmap, me, i, &dir);
if (ourchan->cupdate_off[!dir] == 0)
continue;
peer = gossmap_nth_node(gossmap, ourchan, !dir);
cap = peer_capacity(gossmap, me, peer, ourchan);
if (amount_msat_greater(cap, best_cap)) {
best_num = i;
best_cap = cap;
}
}
return best_num;
}
static struct command_result *
listpeerchannels_listincoming_done(struct command *cmd,
const char *method,
@@ -470,6 +498,7 @@ listpeerchannels_listincoming_done(struct command *cmd,
struct gossmap_node *me;
struct gossmap *gossmap;
struct gossmap_localmods *mods;
size_t deterministic_candidate = 0;
/* Get local knowledge */
mods = gossmods_from_listpeerchannels(tmpctx, &local_id,
@@ -487,6 +516,9 @@ listpeerchannels_listincoming_done(struct command *cmd,
if (!me)
goto done;
if (randbytes_overridden())
deterministic_candidate = best_candidate(gossmap, me);
for (size_t i = 0; i < me->num_chans; i++) {
struct node_id peer_id;
int dir;
@@ -499,6 +531,10 @@ listpeerchannels_listincoming_done(struct command *cmd,
/* Entirely missing? Ignore. */
if (ourchan->cupdate_off[!dir] == 0)
continue;
if (randbytes_overridden() && i != deterministic_candidate)
continue;
/* We used to ignore if the peer said it was disabled,
* but we have a report of LND telling us our unannounced
* channel is disabled, so we still use them. */