askrene: refine: add a step to increase flows ...
What changed, and why it matters
This commit adds a small optimization to Core Lightning's payment routing plugin (askrene). After computing payment routes, if the deliverable amount is slightly short of the requested amount, the code now nudges each route's amount upward by up to 2% (without exceeding each route's maximum). The goal is to avoid an extra expensive computation and to handle edge cases where a channel's minimum HTLC size is larger than the leftover shortfall. There is no indication in the commit that this fixes a security vulnerability.
Review the new `increase_flows()` logic for integer-overflow and rounding-edge cases, especially the hard-coded 0.02 tolerance and the interaction with the following HTLC-min violation loop. Consider whether the tolerance should be configurable and whether aborting on arithmetic failure is the desired failure mode for a plugin.
Security signals we found
New amount arithmetic in routing plugin with hard-coded 2% tolerance
Function aborts on `amount_msat_add`/`amount_msat_sub`/`amount_msat_accumulate` failure, which could crash the plugin on unexpected overflow/underflow
No explicit bounds check that `tolerance` is non-negative or finite before scaling
No changelog or security disclosure in commit message
Evidence from the diff
The patch introduces increase_flows() in plugins/askrene/refine.c. It is called after remove_excess() inside refine_flows(). The function iterates over flows sorted by descending deliver amount and adds to each flow up to tolerance (hard-coded 0.02) of its current deliver amount, capped by the remaining defect and by max_deliverable[index]. It uses existing amount-math helpers (amount_msat_scale, amount_msat_sub, amount_msat_add, amount_msat_accumulate, amount_msat_min) and aborts on arithmetic failure. The subsequent HTLC-min violation detection loop remains unchanged.
Changed components
plugins/askrene/refine.cCore Lightning askrene payment routing refinementInspect captured patch +56 / −0
diff --git a/plugins/askrene/refine.c b/plugins/askrene/refine.c
index 0d1a2c2f..d6e785e5 100644
--- a/plugins/askrene/refine.c
+++ b/plugins/askrene/refine.c
@@ -873,6 +873,58 @@ static struct amount_msat remove_excess(struct flow **flows,
return all_deliver;
}
+/* It increases the flows to meet the deliver target. It does not increase any
+ * flow beyond the tolerance fraction. It doesn't increase any flow above its
+ * max_deliverable value.
+ * Returns the total delivery amount. */
+static struct amount_msat increase_flows(struct flow **flows,
+ size_t **flows_index,
+ struct amount_msat deliver,
+ double tolerance,
+ struct amount_msat *max_deliverable)
+{
+ if (tal_count(flows) == 0)
+ return AMOUNT_MSAT(0);
+
+ struct amount_msat all_deliver, defect;
+ all_deliver = sum_all_deliver(flows, *flows_index);
+
+ /* early exit: target is already met */
+ if (!amount_msat_sub(&defect, deliver, all_deliver) ||
+ amount_msat_is_zero(defect))
+ return all_deliver;
+
+ asort(*flows_index, tal_count(*flows_index), revcmp_flows, flows);
+
+ all_deliver = AMOUNT_MSAT(0);
+ for (size_t i = 0;
+ i < tal_count(*flows_index) && !amount_msat_is_zero(defect); i++) {
+ const size_t index = (*flows_index)[i];
+ struct flow *flow = flows[index];
+ struct amount_msat can_add = defect, amt;
+
+ /* no more than tolerance */
+ if (!amount_msat_scale(&amt, flow->delivers, tolerance))
+ continue;
+ else
+ can_add = amount_msat_min(can_add, amt);
+
+ /* no more than max_deliverable */
+ if (!amount_msat_sub(&amt, max_deliverable[index],
+ flow->delivers))
+ continue;
+ else
+ can_add = amount_msat_min(can_add, amt);
+
+ if (!amount_msat_add(&flow->delivers, flow->delivers,
+ can_add) ||
+ !amount_msat_sub(&defect, defect, can_add) ||
+ !amount_msat_accumulate(&all_deliver, flow->delivers))
+ abort();
+ }
+ return all_deliver;
+}
+
static void write_selected_flows(const tal_t *ctx, size_t *flows_index,
struct flow ***flows)
{
@@ -931,6 +983,10 @@ const char *refine_flows(const tal_t *ctx, struct route_query *rq,
/* remove excess from MCF granularity if any */
remove_excess(*flows, &flows_index, deliver);
+ /* increase flows if necessary to meet the target */
+ increase_flows(*flows, &flows_index, deliver, /* tolerance = */ 0.02,
+ max_deliverable);
+
/* detect htlc_min violations */
for (size_t i = 0; i < tal_count(flows_index);) {
size_t k = flows_index[i];
Why this scored 24/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.