askrene: explain failure: add known enabled case
What changed, and why it matters
This commit improves an internal diagnostic message inside Core Lightning's payment routing plugin (askrene). It adds a new explanation category called 'known_usable' so that when a payment fails, the software can more precisely report that the failure was due to insufficient usable liquidity that is both known to the node and currently enabled. There is no indication this fixes a security vulnerability; it is a correctness/usability improvement to failure reporting.
No security action required. Treat as a normal code-quality/diagnostic improvement. If deploying, verify that askrene failure messages still render correctly and that the new known_usable branch is covered by existing tests.
Security signals we found
No security-relevant signals detected in the diff or commit message.
Change is a diagnostic/UX improvement to failure explanation strings.
No memory safety fixes, input validation changes, or cryptographic changes.
No changelog entry and no security advisory language from the vendor.
Evidence from the diff
The patch modifies plugins/askrene/child/explain_failure.c. It introduces a new struct stat member known_usable in struct node_stats and updates the invariant comments to show total >= gossip_known >= enabled >= known_usable and total >= max_capacity_known >= known_usable. The node_stats() function is reordered so that known_usable is populated only when a channel direction is both gossip-known and enabled, using the most-constraining maximum capacity. A new branch in check_capacity() returns a more specific ‘NO_USABLE_PATHS_STRING’ diagnostic when the requested amount exceeds stats.known_usable.capacity. The change is additive and does not alter payment logic, cryptographic checks, or network behavior.
Changed components
plugins/askrene/child/explain_failure.cCore Lightning askrene payment routing failure explanation logicInspect captured patch +29 / −8
diff --git a/plugins/askrene/child/explain_failure.c b/plugins/askrene/child/explain_failure.c
index a11179d1..1796804d 100644
--- a/plugins/askrene/child/explain_failure.c
+++ b/plugins/askrene/child/explain_failure.c
@@ -83,7 +83,13 @@ struct stat {
};
struct node_stats {
- struct stat total, gossip_known, max_capacity_known, enabled;
+ /* Possible explanations to failure to send/receive funds.
+ * These stats satisfy:
+ * total >= gossip_known >= enabled >= known_usable
+ * total >= max_capacity_known >= known_usable
+ * so that when seeking the main cause we need to go from "total" down
+ * to "known_usable". */
+ struct stat total, gossip_known, max_capacity_known, enabled, known_usable;
};
enum node_direction {
@@ -133,13 +139,6 @@ static const struct layer **node_stats(const tal_t *ctx,
if (node_direction == INTO_NODE)
scidd.dir = !scidd.dir;
- add_stat(&stats->total, cap_msat);
- if (gossmap_chan_set(c, scidd.dir)) {
- add_stat(&stats->gossip_known, cap_msat);
- if (c->half[scidd.dir].enabled)
- add_stat(&stats->enabled, cap_msat);
- }
-
min = AMOUNT_MSAT(0);
max = cap_msat;
constrainer = NULL;
@@ -151,7 +150,18 @@ static const struct layer **node_stats(const tal_t *ctx,
}
if (constrainer && !layer_in_array(most_constraining, constrainer))
tal_arr_expand(&most_constraining, constrainer);
+
+ add_stat(&stats->total, cap_msat);
add_stat(&stats->max_capacity_known, max);
+
+ if (gossmap_chan_set(c, scidd.dir)) {
+ add_stat(&stats->gossip_known, cap_msat);
+ if (c->half[scidd.dir].enabled) {
+ add_stat(&stats->enabled, cap_msat);
+ add_stat(&stats->known_usable, max);
+ }
+ }
+
}
return most_constraining;
}
@@ -246,6 +256,17 @@ static const char *check_capacity(const tal_t *ctx,
fmt_amount_msat(tmpctx, stats.enabled.capacity),
fmt_amount_msat(tmpctx, stats.total.capacity));
}
+ if (amount_msat_greater(amount, stats.known_usable.capacity)) {
+ *total_capacity_failure = false;
+ return child_log(ctx, LOG_DBG,
+ NO_USABLE_PATHS_STRING
+ " We know from %s that %s has maximum usable capacity %s"
+ " (in %zu channels).",
+ format_layer_names(tmpctx, most_constraining),
+ name,
+ fmt_amount_msat(tmpctx, stats.known_usable.capacity),
+ stats.known_usable.num_channels);
+ }
return NULL;
}
Why this scored 17/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.