ElementsProject / ElementsProject/lightning
Feature request: getroutes support for circular routes (source == destination) for rebalancing
- Dominant language
- C
- Stars
- 3.1k
- Forks
- 1k
- Avg merge
- 4d 10h
- Merged PRs (30d)
- 13
Description
## 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](https://github.com/lightning-goats/cl-hive) (fleet coordination) and [cl-revenue-ops](https://github.com/lightning-goats/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
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.