Skip to content

Feature request: getroutes support for circular routes (source == destination) for rebalancing #9032

@santyr

Description

@santyr

Summary

getroutes currently requires source != destination. When source == destination, it returns immediately with no routes (and xpay resolves the payment hash locally without sending HTLCs). This makes it impossible to use askrene layers for circular rebalancing — the primary use case for Lightning channel liquidity management.

Use Case

Circular rebalancing moves liquidity between channels by routing a payment from yourself, through the network, back to yourself. This is the most common operation for node operators maintaining channel health.

With askrene layers, plugins can encode rich routing intelligence:

  • Fleet/peer group channels at reduced fees (cooperative routing)
  • Reputation-based node biases (avoid unreliable peers)
  • Capacity constraints from real-time observations (inform-channel)
  • Profitability-based channel preferences

All of this intelligence is available via getroutes for A→B payments. But for circular rebalancing (A→...→A), none of it can be used because getroutes doesn't support source == destination.

Current Workaround

We use a two-step approach:

  1. getroutes (with layers) for route discovery — finds optimal path and fee estimate
  2. getroute (legacy, no layer support) + sendpay for execution — computes per-hop amounts and sends HTLCs

This works but means the execution path can't benefit from askrene layers. The legacy getroute uses gossip fees which may differ from layer overrides, and doesn't respect biases, capacity constraints, or disabled nodes from layers.

Proposed Enhancement

Allow getroutes to find circular routes when source == destination. The returned route would be a cycle: source → hop1 → hop2 → ... → source.

This would enable:

getroutes(source=our_id, destination=our_id, amount_msat=X, layers=[...])
→ returns circular route with correct per-hop amounts
→ sendpay(route) executes the circular payment

Alternatively, xpay could detect that when layers are provided and a circular route exists, it should route through the network instead of resolving locally.

Context

We develop cl-hive (fleet coordination) and cl-revenue-ops (fee/rebalance optimization) for Core Lightning. We've built a 5-layer askrene intelligence stack (fleet topology, peer reputation, corridor values, traffic patterns, local profitability) that makes getroutes significantly smarter for our fleet. But all of that intelligence is lost for our most frequent operation — rebalancing — because it requires circular routing.

The current sendpay + getroute workaround works but is fragile (manual first-hop fee calculation, no layer awareness for the execution path, different fee math between getroute and getroutes).

Related

  • xpay self-payment resolves locally (test_xpay_selfpay) — correct for single-node but prevents multi-hop circular routing
  • sendpay with explicit routes is the only way to execute circular payments today
  • Sling plugin implements its own Dijkstra for circular pathfinding, bypassing CLN's routing entirely

Metadata

Metadata

Assignees

No one assigned

    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