HTLC (Hash Time-Locked Contract) lifecycle: offered/received, fulfill via preimage, fail/timeout, 2nd-stage txs. Modern variant PTLCs (Point Time-Locked Contracts) with adaptor signatures. USE WHEN: implementing payment forwarding, debugging stuck HTLCs, reasoning about CLTV deltas in routes.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add claude-dev-suite/claude-dev-suite --skill htlcs --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Htlcs?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/claude-dev-suite-htlcs)More formats (shields.io, HTML) on the badges page.
---
name: lightning-htlcs
description: |
HTLC (Hash Time-Locked Contract) lifecycle: offered/received,
fulfill via preimage, fail/timeout, 2nd-stage txs. Modern variant
PTLCs (Point Time-Locked Contracts) with adaptor signatures.
USE WHEN: implementing payment forwarding, debugging stuck HTLCs,
reasoning about CLTV deltas in routes.
allowed-tools: Read, Grep, Glob
---
# HTLCs (Hash Time-Locked Contracts)
The atomicity primitive that makes Lightning routing work. An HTLC
is "claimable with preimage `r` such that hash(r) = H, OR refundable
after CLTV expires".
## Lifecycle
### Sender → first hop
```
Alice → Bob: update_add_htlc(amount, hash, cltv, onion)
```
The onion contains routing data for Bob to forward.
### Forward chain
Bob receives, peels onion, sees instructions to forward to Carol:
```
Bob → Carol: update_add_htlc(amount - bob_fee, hash, cltv - bob_delta, onion')
```
Continues until destination Dave (final hop, onion is empty payload).
### Settlement (cooperative)
Dave reveals preimage `r`:
```
Dave → Carol: update_fulfill_htlc(channel_id, htlc_id, r)
Carol → Bob: update_fulfill_htlc(...) (verifies hash(r) = H, propagates)
Bob → Alice: update_fulfill_htlc(...)
```
Each hop earns its fee (held in the HTLC amount).
### Settlement (failed)
```
Carol → Bob: update_fail_htlc(channel_id, htlc_id, encrypted_failure)
Bob → Alice: update_fail_htlc(...)
```
Failure encryption uses ChaCha20 with onion shared secrets, hop-by-hop
(only Alice can decrypt the original failure).
### Settlement (force-close)
If a peer is unresponsive past timeout → force-close that channel,
HTLC resolved on chain.
## CLTV deltas
The HTLC has CLTV expiry. As the payment travels:
- Sender's HTLC has the highest CLTV.
- Each forwarder reduces CLTV by their `cltv_expiry_delta` parameter.
- Receiver's HTLC has the smallest CLTV.
This ordering ensures:
- Receiver claims at low CLTV.
- Forwarders have time to claim from upstream.
- If anything goes wrong, refunds happen in safe order.
Standard `cltv_expiry_delta`: 40-100 blocks per hop typically.
## HTLC scripts on chain (post force-close)
For an HTLC the channel partner offered to you:
```
hash160(payment_hash) OP_EQUALVERIFY <local_htlc_pk> OP_CHECKSIG
OR
<cltv> OP_CHECKLOCKTIMEVERIFY OP_DROP <remote_htlc_pk> OP_CHECKSIG
OR
<revocation_pubkey> OP_CHECKSIG
```
Three paths: success (preimage), timeout (CLTV), revocation (cheating
detection).
## 2nd-stage HTLC txs
After force-close, HTLC outputs aren't directly spendable — they go
through pre-signed 2nd-stage txs:
- **HTLC-success-tx**: spends HTLC output revealing preimage; output
is `to_local`-style with CSV delay before claimable by you.
- **HTLC-timeout-tx**: spends HTLC output after CLTV; same CSV-delayed
output.
This 2-stage structure is required for legacy commitment because
sigs are pre-committed at channel open (no flexibility post-broadcast).
## Anchor zero-fee variant
`option_anchors_zero_fee_htlc_tx`: HTLC 2nd-stage txs are 0-fee at
sign time, fee-bumped via the commitment's anchors when broadcast.
## PTLCs (Point Time-Locked Contracts)
Modern Schnorr/Taproot-based replacement for HTLCs:
- Instead of `hash(r) = H`, use `r * G = T` (Schnorr point).
- Use **adaptor signatures**: each hop holds a pre-signature
conditional on revealing the secret.
- Privacy gain: each hop sees a different `T` (derived per-hop), so
observers can't correlate hops by hash value.
Status: BOLT update in flight; implementations partial.
## In-flight HTLC limits
Channel-level limits (advertised in `accept_channel`):
- `max_accepted_htlcs` (default ~483).
- `max_htlc_value_in_flight_msat`.
- `dust_limit_satoshis`.
Excess HTLCs cause `update_add_htlc` to fail.
## "Stuck" HTLCs
Causes:
- Downstream node unresponsive (route stuck mid-forward).
- Mempool fee spike preventing on-chain resolution.
- Channel partner offline at force-close time.
Recovery:
- Watchtower or self-monitoring + force-close to reclaim funds.
- Standard fix: wait for CLTV expiry, force-close the upstream
channel, recover via 2nd-stage timeout tx.
## Replacement cycling impact
HTLC-timeout txs are pre-signed at channel open. An attacker could
historically "pin" these via low-fee descendants → honest party can't
broadcast → CLTV passes upstream → loss.
Post TRUC v3 + ephemeral anchors: pinning surface removed.
## Common bugs
- CLTV delta too small → upstream HTLC times out before downstream
resolves → forced refund and lost fees.
- Race condition: peer reveals preimage just before CLTV → both
parties try to claim, possible loss.
- Misencoded onion failure → hop can't decrypt; routing data lost.
- Custom records (BOLT 4 TLV) incorrectly formatted → some impls
reject HTLCs.
## See also
- [channels/SKILL.md](../channels/SKILL.md)
- [routing/SKILL.md](../routing/SKILL.md)
- [onion/SKILL.md](../onion/SKILL.md)
- [replacement-cycling/SKILL.md](../replacement-cycling/SKILL.md)
- [../../cryptography/adaptor-sigs/SKILL.md](../../cryptography/adaptor-sigs/SKILL.md)
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!