Skip to content
Closed
12 changes: 10 additions & 2 deletions channeld/channeld.c
Original file line number Diff line number Diff line change
Expand Up @@ -6412,6 +6412,8 @@ static void init_channel(struct peer *peer)
struct penalty_base *pbases;
struct channel_type *channel_type;
bool found_locked_inflight;
bool has_funding_short_id;
struct short_channel_id *funding_short_id;

assert(!(fcntl(MASTER_FD, F_GETFL) & O_NONBLOCK));

Expand Down Expand Up @@ -6449,9 +6451,9 @@ static void init_channel(struct peer *peer)
&peer->revocations_received,
&peer->htlc_id,
&htlcs,
&peer->channel_ready[LOCAL],
&has_funding_short_id,
&peer->channel_ready[REMOTE],
&peer->short_channel_ids[LOCAL],
&funding_short_id,
&reconnected,
&peer->send_shutdown,
&peer->shutdown_sent[REMOTE],
Expand All @@ -6471,6 +6473,12 @@ static void init_channel(struct peer *peer)
master_badmsg(WIRE_CHANNELD_INIT, msg);
}

if (has_funding_short_id && funding_short_id) {
peer->short_channel_ids[LOCAL] = *funding_short_id;
} else {
peer->short_channel_ids[LOCAL] = peer->local_alias;
}

peer->final_index = tal_dup(peer, u32, &final_index);
peer->final_ext_key = tal_dup(peer, struct ext_key, &final_ext_key);
peer->splice_state->count = tal_count(peer->splice_state->inflights);
Expand Down
4 changes: 2 additions & 2 deletions channeld/channeld_wire.csv
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ msgdata,channeld_init,revocations_received,u64,
msgdata,channeld_init,next_htlc_id,u64,
msgdata,channeld_init,num_existing_htlcs,u16,
msgdata,channeld_init,htlcs,existing_htlc,num_existing_htlcs
msgdata,channeld_init,local_channel_ready,bool,
msgdata,channeld_init,has_funding_short_id,bool,
msgdata,channeld_init,remote_channel_ready,bool,
msgdata,channeld_init,funding_short_id,short_channel_id,
msgdata,channeld_init,funding_short_id,?short_channel_id,
msgdata,channeld_init,reestablish,bool,
msgdata,channeld_init,send_shutdown,bool,
msgdata,channeld_init,remote_shutdown_received,bool,
Expand Down
8 changes: 4 additions & 4 deletions contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion contrib/pyln-grpc-proto/pyln/grpc/node_pb2_grpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from pyln.grpc import node_pb2 as node__pb2

GRPC_GENERATED_VERSION = '1.69.0'
GRPC_GENERATED_VERSION = '1.74.0'
GRPC_VERSION = grpc.__version__
_version_not_supported = False

Expand Down
8 changes: 4 additions & 4 deletions contrib/pyln-grpc-proto/pyln/grpc/primitives_pb2.py

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

81 changes: 81 additions & 0 deletions doc/orphaned-channel-cleanup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Orphaned Channel Cleanup Process

## Overview
Channels can become "orphaned" when they get stuck in the `CHANNELD_AWAITING_LOCKIN` state with a funding transaction that never confirms. This can happen if:
- The funding transaction was never broadcast
- The funding transaction was dropped from the mempool
- The transaction fee was too low and it got purged

## Detection Commands

### listorphanedchannels
Lists channels in `CHANNELD_AWAITING_LOCKIN` state where the funding transaction is not in the mempool.

```bash
lightning-cli listorphanedchannels [timeout_hours=48]
```

Parameters:
- `timeout_hours` (optional): Only show channels stuck for at least this many hours (default: 48)

Returns:
- Array of orphaned channels with details including peer_id, channel_id, funding_txid, hours_stuck
- Total count of orphaned channels

### cleanuporphanedchannels
Safely removes orphaned channels that have been stuck for the specified time.

```bash
lightning-cli cleanuporphanedchannels [timeout_hours=48] [force=false]
```

Parameters:
- `timeout_hours` (optional): Only cleanup channels stuck for at least this many hours (default: 48)
- `force` (optional): Force cleanup even if safety checks fail (default: false)

Safety checks:
- Channel must not have any pending HTLCs
- Channel must be in `CHANNELD_AWAITING_LOCKIN` state

## Manual Cleanup Process

1. First, identify orphaned channels:
```bash
lightning-cli listorphanedchannels
```

2. Review each orphaned channel carefully:
- Check the funding transaction status on a block explorer
- Verify no funds are at risk

3. Clean up individual channels using dev-forget-channel:
```bash
lightning-cli dev-forget-channel <peer_id> [short_channel_id] [force=true]
```

4. Or clean up all orphaned channels at once:
```bash
lightning-cli cleanuporphanedchannels
```

## Monitoring

The node will log warnings when orphaned channels are detected:
```
UNUSUAL: Orphaned channel detected: funding_txid=xxx, outnum=0, stuck for 72 hours
```

## Prevention

To prevent orphaned channels:
1. Ensure funding transactions use appropriate fees
2. Monitor channel states after funding
3. Set up alerts for channels stuck in `CHANNELD_AWAITING_LOCKIN`
4. Consider implementing automatic cleanup policies

## Recovery

If you accidentally cleanup a channel with a valid funding transaction:
1. The funds remain safe in the funding output
2. You can spend the funding output using the commitment transaction
3. Contact support if you need assistance recovering funds
6 changes: 3 additions & 3 deletions lightningd/channel_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -1671,7 +1671,7 @@ bool peer_start_channeld(struct channel *channel,
u8 *initmsg;
int hsmfd;
const struct existing_htlc **htlcs;
struct short_channel_id scid;
struct short_channel_id *scid;
u64 num_revocations;
struct lightningd *ld = channel->peer->ld;
const struct config *cfg = &ld->config;
Expand Down Expand Up @@ -1727,10 +1727,10 @@ bool peer_start_channeld(struct channel *channel,
htlcs = peer_htlcs(tmpctx, channel);

if (channel->scid) {
scid = *channel->scid;
scid = channel->scid;
log_debug(channel->log, "Already have funding locked in");
} else {
memset(&scid, 0, sizeof(scid));
scid = NULL;
}

num_revocations = revocations_received(&channel->their_shachain.chain);
Expand Down
Loading