askrene: refine: disable HTLC min violations

Disable channels with HTLC min violations so that we don't hit them
twice when computing routes.

Changelog-None

Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
This commit is contained in:
Lagrang3
2025-07-09 16:05:11 +01:00
committed by Rusty Russell
parent f4f8ea08ff
commit b479963c09
4 changed files with 67 additions and 6 deletions

View File

@@ -564,6 +564,13 @@ static struct command_result *do_getroutes(struct command *cmd,
/* we temporarily apply localmods */
gossmap_apply_localmods(askrene->gossmap, localmods);
/* I want to be able to disable channels while working on this query.
* Layers are for user interaction and cannot be used for this purpose.
*/
rq->disabled_chans =
tal_arrz(rq, bitmap,
2 * BITMAP_NWORDS(gossmap_max_chan_idx(askrene->gossmap)));
/* localmods can add channels, so we need to allocate biases array
* *afterwards* */
rq->biases =

View File

@@ -2,6 +2,7 @@
#define LIGHTNING_PLUGINS_ASKRENE_ASKRENE_H
#include "config.h"
#include <bitcoin/short_channel_id.h>
#include <ccan/bitmap/bitmap.h>
#include <ccan/htable/htable_type.h>
#include <ccan/list/list.h>
#include <common/amount.h>
@@ -60,6 +61,9 @@ struct route_query {
/* Additional per-htlc cost for local channels */
const struct additional_cost_htable *additional_costs;
/* channels we disable during computation to meet constraints */
bitmap *disabled_chans;
};
/* Given a gossmap channel, get the current known min/max */

View File

@@ -319,6 +319,15 @@ static void set_capacity(s64 *capacity, u64 value, u64 *cap_on_capacity)
*cap_on_capacity -= *capacity;
}
/* Helper to check whether a channel is available */
static bool channel_is_available(const struct route_query *rq,
const struct gossmap_chan *chan, const int dir)
{
const u32 c_idx = gossmap_chan_idx(rq->gossmap, chan);
return gossmap_chan_set(chan, dir) && chan->half[dir].enabled &&
!bitmap_test_bit(rq->disabled_chans, c_idx * 2 + dir);
}
/* FIXME: unit test this */
/* The probability of forwarding a payment amount given a high and low liquidity
* bounds.
@@ -568,7 +577,7 @@ static void init_linear_network(const tal_t *ctx,
const struct gossmap_chan *c = gossmap_nth_chan(gossmap,
node, j, &half);
if (!gossmap_chan_set(c, half) || !c->half[half].enabled)
if (!channel_is_available(params->rq, c, half))
continue;
/* If a channel insists on more than our total, remove it */
@@ -644,7 +653,7 @@ struct chan_flow
* */
static struct node find_path_or_cycle(
const tal_t *working_ctx,
const struct gossmap *gossmap,
const struct route_query *rq,
const struct chan_flow *chan_flow,
const struct node source,
const s64 *balance,
@@ -653,6 +662,7 @@ static struct node find_path_or_cycle(
int *prev_dir,
u32 *prev_idx)
{
const struct gossmap *gossmap = rq->gossmap;
const size_t max_num_nodes = gossmap_max_node_idx(gossmap);
bitmap *visited =
tal_arrz(working_ctx, bitmap, BITMAP_NWORDS(max_num_nodes));
@@ -671,7 +681,7 @@ static struct node find_path_or_cycle(
const struct gossmap_chan *c =
gossmap_nth_chan(gossmap, cur, i, &dir);
if (!gossmap_chan_set(c, dir) || !c->half[dir].enabled)
if (!channel_is_available(rq, c, dir))
continue;
const u32 c_idx = gossmap_chan_idx(gossmap, c);
@@ -877,7 +887,7 @@ get_flow_paths(const tal_t *ctx,
while (balance[source.idx] < 0) {
prev_chan[source.idx] = NULL;
struct node sink = find_path_or_cycle(
working_ctx, params->rq->gossmap, chan_flow, source,
working_ctx, params->rq, chan_flow, source,
balance, prev_chan, prev_dir, prev_idx);
if (balance[sink.idx] > 0)
@@ -1107,8 +1117,7 @@ static void init_linear_network_single_path(
gossmap_nth_chan(gossmap, node, j, &half);
struct amount_msat mincap, maxcap;
if (!gossmap_chan_set(c, half) ||
!c->half[half].enabled)
if (!channel_is_available(params->rq, c, half))
continue;
/* If a channel cannot forward the total amount we don't

View File

@@ -735,6 +735,38 @@ static struct amount_msat path_min_deliverable(struct channel_data *path)
return least_destination;
}
static const char *
remove_htlc_min_violations(const tal_t *ctx, struct route_query *rq,
const struct flow *flow,
const struct channel_data *channels)
{
const char *error_message = NULL;
struct amount_msat msat = flow->delivers;
for (size_t i = tal_count(flow->path) - 1; i < tal_count(flow->path);
i--) {
if (amount_msat_less(msat, channels[i].htlc_min)) {
rq_log(
ctx, rq, LOG_INFORM,
"Sending %s across %s would violate htlc_min "
"(~%s), disabling this channel",
fmt_amount_msat(ctx, msat),
fmt_short_channel_id_dir(ctx, &channels[i].scidd),
fmt_amount_msat(ctx, channels[i].htlc_min));
bitmap_set_bit(rq->disabled_chans, channels[i].idx);
break;
}
if (!amount_msat_add_fee(
&msat, channels[i].fee_base_msat,
channels[i].fee_proportional_millionths)) {
error_message =
rq_log(ctx, rq, LOG_BROKEN,
"%s: Adding fee to amount", __func__);
break;
}
}
return error_message;
}
static struct amount_msat sum_all_deliver(struct flow **flows,
size_t *flows_index)
{
@@ -819,6 +851,7 @@ const char *refine_flows(const tal_t *ctx, struct route_query *rq,
struct amount_msat deliver, struct flow ***flows)
{
const tal_t *working_ctx = tal(ctx, tal_t);
const char *error_message = NULL;
struct amount_msat *max_deliverable;
struct amount_msat *min_deliverable;
struct channel_data **channel_mpp_cache;
@@ -863,6 +896,10 @@ const char *refine_flows(const tal_t *ctx, struct route_query *rq,
}
/* htlc_min is not met for this flow */
tal_arr_remove(&flows_index, i);
error_message = remove_htlc_min_violations(
working_ctx, rq, (*flows)[k], channel_mpp_cache[k]);
if (error_message)
goto fail;
}
/* remove 0 amount flows if any */
@@ -889,4 +926,8 @@ const char *refine_flows(const tal_t *ctx, struct route_query *rq,
tal_free(working_ctx);
return NULL;
fail:
tal_free(working_ctx);
return error_message;
}