Skip to content

askrene: clamp impression-adjusted min/max to channel capacity #9375

Description

@nGoline

Follow-up from #9150.

layer_apply_constraints() applies impressions without bounding the result:

  • reverse direction: min and max both grow by imp->amount, saturating at UINT64_MAX, with no clamp to the channel's gossmap capacity
  • forward direction: both shrink by imp->amount, saturating at 0

Since #9150, get_constraints() seeds *max from gossmap_chan_get_capacity() instead of -1ULL, so reverse-direction impressions can push max above the channel's real capacity, and enough forward-direction volume drives max to 0 and makes askrene treat a live channel as dead.

The FIXME added to test_xpay_fake_channeld in that PR documents the symptom:

/* FIXME: We fail on #10, due mainly to a buildup of usage on 0x2134x0/0:
 * Failed: We could not find a usable set of paths. The shortest path is
 * 103x1x0->0x2134x0->1725x11x1725, but 0x2134x0/0 exceeds htlc_maximum_msat ~1000448msat
 */

Bounded in practice by two things: mcf.c defensively does if (min > max) min = max, and xpay calls askrene-age with a one-hour cutoff before every payment, so impressions expire. But the layer state is still incoherent in the meantime, and explain_failure.c documents an invariant (total >= max_capacity_known >= known_usable) that impressions can violate, since max can exceed cap_msat.

Minimum fix: clamp max to the channel capacity and min to max inside layer_apply_constraints().

Broader question, raised by Lagrang3 during review: the magnitude by which impressions move the bars is too aggressive. A successful payment proves liquidity of at least amount existed, so subtracting the full amount from max discards information rather than adding it. Worth revisiting the model alongside the clamp.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions