ElementsProject / ElementsProject/lightning

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

Open
#9,032 4 comments 0 reactions 0 assignees View on GitHub
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.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.