Hermes / HTLC Second-Stage

HTLC Second-Stage: closing with a payment in flight

When a channel force-closes unilaterally, its balance outputs are easy, but an HTLC that's still pending can't be swept with a bare signature. It's spent by a pre-signed second-stage transaction: an HTLC-timeout for an HTLC you offered, an HTLC-success for one you received. Both are 2-of-2, and (the elegant part) both pay into a to_local output, so demo 13's delay + revocation penalty applies one level down. Built from scratch and checked byte-for-byte against BOLT-3 Appendix C.

1 · Which HTLC got stuck?

commitment txforce-closed on-chain
→
HTLC-timeout2-of-2, timelocked
→
to_localdelayed + revocable

2 · The second-stage transaction

type · nLockTime · input
HTLC-timeout  ·  locktime 502  ·  spends HTLC output #1 (2000 sat)
witness stack  · the empty item fails OP_SIZE 32, so the offered script takes its 2-of-2 timeout branch
    output: a to_local P2WSH (this node's HTLC value, now delayed)
    –
    ✓ reproduces BOLT-3 Appendix C byte-for-byte
    –

    3 · …and the output is itself a to_local

    The second stage doesn't hand the money over; it drops it into the same delay/revocation script as the main balance. Drag the blocks elapsed since the second-stage tx confirmed:

    blocks since confirmation: 0 to_self_delay = 144

    The second-stage output's to_local

    Identical to demo 13's balance output: the IF branch is the counterparty's instant justice with the revocation key; the ELSE branch is this node's own funds, spendable only after to_self_delay. A pending payment doesn't escape the penalty. It inherits it.

    revocationPubkey
    –
    local_delayedPubkey (this node, after 144 blocks)
    –

    Why a 2-of-2?

    Every second-stage tx carries <remotehtlcsig>: a signature the counterparty gave in advance for this exact transaction. That's what binds the local node to the rules: it can't rewrite the HTLC-timeout to an earlier nLockTime (the signature would break), and it can't skip the delayed output. Cooperation up front, enforced on-chain later.

    HTLC-timeout: nLockTime = cltv_expiry  →  can't reclaim early
    HTLC-success: nLockTime = 0  +  preimage in the witness
    Things to notice

    Two stages, one reason

    The HTLC has to stay claimable (by preimage) or refundable (by timeout) even while the owner's funds are frozen for to_self_delay. Splitting it into a second transaction is what lets both timelocks coexist.

    The penalty recurses

    Because the second-stage output is a to_local, a second-stage tx from a revoked commitment is just as punishable: the counterparty's revocation key sweeps it before the delay: demo 13's justice, one level deeper.

    Byte-for-byte real

    These aren't toy scripts. The transactions above match the official HTLC-timeout / HTLC-success test vectors in BOLT-3 Appendix C exactly: same keys, same RFC 6979 signatures, same bytes on the wire.