Skip to content

Instantly share code, notes, and snippets.

@instagibbs
Last active May 3, 2025 14:06
Show Gist options
  • Save instagibbs/1d02d0251640c250ceea1c66665ec163 to your computer and use it in GitHub Desktop.
Save instagibbs/1d02d0251640c250ceea1c66665ec163 to your computer and use it in GitHub Desktop.
PTLCs for LN

Messaging Changes

I am assuming minimal changes to commitment transaction structure, essentially swapping out HTLC for PTLC, so no fast-forward schemes here.

Here are output labels because I get confused so often what things in BOLTs mean:

  • (a) a_o_atx_* Alice-offered "offered PTLC" in Alice's tx
  • (b) a_o_btx_* Alice-offered "received PTLC" in Bob's tx
  • (c) b_o_atx_* Bob-offered "received PTLC" in Alice's tx
  • (d) b_o_btx_* Bob-offered "offered PTLC" in Bob's tx

*_presign means single-signature adaptors for PTLC-Success transactions
*_psig means partial signature for MuSig2 for PTLC-Success transactions
*_nonce means nonce for _psig (TBD, didn't bother filling these out)
*_signed means BIP340 signature for PTLC-Timeout transactions

done is just a message to prompt a response before sending commitment_signed

Messages are maximally split up for understanding, and to analyze round-trips and complexity. This is not a concrete proposal for bits on the wire.

Single-sig adaptors, sync updates

    +-------+                                 +-------+
    |       |                                 |       | Alice's turn
    |       |--(1)---- update_offer_ptlc ---->|       | new amounts, lock info
    |       |--(2)---- update_offer_ptlc ---->|       | new amounts, lock info
    |       |--(3)---- done------------------>|       | Prompt Bob to send (d) adaptor sigs before Alice sends commit tx sig
    |       |                                 |       |
    |       |<-(4)--- b_o_btx_presign---------|       | Bob re-commits to all (d) PTLC-Success paths (must be before commit)
    |       |                                 |       | 
    |       |--(5)--- commitment_signed ----->|       | Alice's B_commit sig
    |       |--(6)--- b_o_btx_sign----------->|       | Alice's full sig for (d) PTLC-timeouts back to Bob
    |       |--(7)--- a_o_btx_presign-------->|       | Alice's adaptor sig for (b)
    |       |--(8)--- a_o_atx_presign-------->|       | Alice re-commits to all (a) PTLC-S paths with adaptor sigs
    |       |                                 |       |
    |   A   |<-(9)--- revoke_and_ack ---------|   B   | Bob can broadcast latest state safely, revoke older
    |       |<-(10)- commitment_signed -------|       | Bob's A_commit sig
    |       |<-(11)--b_o_atx_presign----------|       | Bob's adaptor signatures for (c)
    |       |<-(12)--a_o_atx_sign-------------|       | Bob's full sig for (a) PTLC-timeouts back to Alice
    |       |                                 |       |
    |       |--(13)-- revoke_and_ack -------->|       | Alice is now committed, Bob can now safely forward
    |       |                                 |       |
    +-------+                                 +-------+

Rationale

(3) is necessary to prompt sending of b_o_btx_presign at (4). If Alice sends commitment_signed (5) without (4) being received, then Bob can take his commitment transaction to chain, and retroactively "uncommit" PTLCs he has previously offered in prior updates, which Alice has forwarded.

(6)/(7) is the rest of the (adaptor) signatures to complete Bob's transaction.

Since we have synchronous updates, a_o_atx_presign (8) can be sent by Alice any time after done(3) since we already know the complete structure Alice's commitment transaction by (3), and therefore the sighashes for all PTLCs.

Alice must send a_o_atx_presign(8) before commitment_signed(10) can be sent anyways, for the same reason as (3).

This results in 2.5 RTT for a PTLC forward.

Single-sig adaptors, async updates

If we allow asynchronous updates(without ANYPREVOUT-like functionality), we can not predict the commitment transaction structure on "our side" until the other side signals they are done changing it.

    +-------+                                 +-------+
    |       |<-(0)---- update_offer_ptlc -----|       | new amounts, lock info
    |       |                                 |       |
    |       |--(1)---- update_offer_ptlc ---->|       | new amounts, lock info
    |       |--(2)---- update_offer_ptlc ---->|       | new amounts, lock info
    |       |--(3)---- done------------------>|       | Prompt Bob to send (d) adaptor sigs before Alice sends commit tx sig
    |       |                                 |       |
    |       |<-(4)--- b_o_btx_presign---------|       | Bob re-commits to all (d) PTLC-Success paths (must be before commit)
    |       |                                 |       |
    |       |--(5)--- commitment_signed ----->|       | Alice's B_commit sig
    |       |--(6)--- a_o_btx_presign-------->|       | Alice's presignatures for (b)
    |       |--(7)--- b_o_btx_sign----------->|       | Alice's full sig for (d)
    |       |                                 |       |
    |   A   |<-(8)--- revoke_and_ack ---------|   B   | Bob can broadcast local latest state safely, revoke older
    |       |<-(9)--- done--------------------|       | Bob prompts Alice to re-commit to (a)
    |       |                                 |       |
    |       |--(10)-- a_o_atx_presign-------->|       | Alice re-commits to all (a) PTLC-Success paths (must be before commit)
    |       |                                 |       |
    |       |<-(11)- commitment_signed -------|       | Bob's A_commit sig, Bob's presignatures for (c) and full sig for (a)
    |       |<-(12)-- b_o_atx_presign---------|       | Bob's presignatures for (c)
    |       |<-(13)-- a_o_atx_sign------------|       | Bob's full sig for (d)
    |       |                                 |       |
    |       |--(14)-- revoke_and_ack -------->|       | Alice is now committed, Bob can now safely forward
    |       |                                 |       |
    +-------+                                 +-------+

Rationale

a_o_atx_presign(10) must happen after done(9), in other words after all Bob-intitiated updates are done which adds another round-trip, resulting in 3.5 RTT per PTLC forward.

The rest of the protocol stays the same.

MuSig2-based adaptor signatures, sync updates

The key difference for this construction is that it necessitates additional round-trips since the receiving party must offer a partial signature first to maintain fairness in the exchange protocol.

Let's ignore all nonces for the sake of simplicity, assuming they can be preshared optimally.

    +-------+                                 +-------+ Alice's turn
    |       |                                 |       |
    |       |--(1)---- update_offer_ptlc ---->|       | new amounts, lock info
    |       |--(2)---- update_offer_ptlc ---->|       | new amounts, lock info
    |       |--(3)---- b_o_btx_psig---------->|       | Alice psigning Bob-offered PTLC in Bob tx (d) 
    |       |--(4)---- done------------------>|       | Prompt Bob to send (d) adaptor sigs before Alice sends commit tx sig
    |       |                                 |       |
    |       |<-(5)--- b_o_btx_psig------------|       | Bob re-commits to all (d) PTLC-Success paths (must be before commit)
    |       |                                 |       |
    |       |--(6)--- commitment_signed ----->|       | Bob knows full local commit tx sig for Alice (and Alice knows PTLC-S (d))
    |       |                                 |       |
    |       |<-(7)--- a_o_atx_psig------------|       | Bob psigning Alice-offered PTLC in Alice tx (a)
    |       |<-(8)--- a_o_btx_psig------------|       | Bob psigning Alice-offered PTLC in Bob tx (b)
    |       |                                 |       |
    |       |--(9)--- a_o_atx_psig----------->|       | Alice psigning Alice-offered PTLC in Alice tx (a)
    |       |--(10)-- a_o_btx_psig----------->|       | Alice psigning Alice-offered PTLC in Bob tx (b)
    |       |--(11)-- b_o_atx_psig----------->|       | Alice psigning Bob-offered PTLC in Alice tx (c) 
    |       |                                 |       |
    |       |<-(12)---b_o_atx_psig------------|       | Bob psigning Bob-offered PTLC in Alice tx (c)
    |   A   |<-(13)---revoke_and_ack ---------|   B   | All Alice-offered PTLCs locked in, new tx safe for Bob
    |       |<-(14)-- commitment_signed ------|       | Alice now knows Bob's sigs for Alice's commit tx
    |       |                                 |       | Bob wasn't allowed to add his own PTLCs so Alice can finish up
    |       |--(15)--- revoke_and_ack ------->|       | Alice is now committed, Bob can now safely forward
    |       |                                 |       |
    +-------+                                 +-------+

Rationale

b_o_*tx_psig(3) must be sent by Alice first, and conversely a_o_*tx_psig(3) must be sent by Bob first. This results in 3.5 RTT.

MuSig2-based adaptor signatures, async updates

With asynchronous updates, again, Bob will not be able to predict the structure of Alice's commitment transaction as early, resulting in an additional round-trip. Nonce message patterns are left as an exercise for the reader.

    +-------+                                 +-------+
    |       |<-(0)---- update_offer_ptlc -----|       | new amounts, lock info
    |       |                                 |       |
    |       |--(1)---- update_offer_ptlc ---->|       | new amounts, lock info
    |       |--(2)---- update_offer_ptlc ---->|       | new amounts, lock info
    |       |--(3)--- b_o_btx_psig----------->|       | Alice psigning Bob-offered PTLC in Bob tx (d) 
    |       |--(4)---- done------------------>|       | Prompt Bob to send (d) adaptor sigs before Alice sends commit tx sig
    |       |                                 |       |
    |       |<-(5)--- b_o_btx_psig------------|       | Bob re-commits to all (d) PTLC-Success paths (must be before commit)
    |       |                                 |       |
    |       |--(6)--- commitment_signed ----->|       | Bob knows full local commit tx sig for Alice and Alice knows (d)
    |       |                                 |       |
    |       |<-(7)--- a_o_btx_psig------------|       | Bob psigning Alice-offered PTLC in Bob tx (b)
    |       |                                 |       |
    |       |--(8)--- a_o_btx_psig----------->|       | Alice psigning Alice-offered PTLC in Bob tx (b)
    |       |                                 |       |
    |   A   |<-(9)---- revoke_and_ack --------|   B   | All Alice-offered PTLCs locked in, new tx safe for Bob
    |       |<-(10)--- done ------------------|       | Bob is done, atx is fixed, psig time
    |       |<-(11)-- a_o_atx_psig------------|       | Bob psigning Alice-offered PTLC in Alice tx (a)
    |       |                                 |       |
    |       |--(10)-- a_o_atx_psig----------->|       | Alice psigning Alice-offered PTLC in Alice tx (a)
    |       |--(11)-- b_o_atx_psig----------->|       | Alice psigning Bob-offered PTLC in Alice tx (c) 
    |       |                                 |       |
    |       |<-(12)--- b_o_atx_psig-----------|       | Bob psigning Bob-offered PTLC in Alice tx (c)
    |       |<-(13)-- commitment_signed ------|       | Alice now knows Bob's sigs for Alice's commit tx
    |       |                                 |       | Bob wasn't allowed to add his own PTLCs so Alice can finish up
    |       |--(14)--- revoke_and_ack ------->|       | Alice is now committed, Bob can now safely forward
    |       |                                 |       |
    +-------+                                 +-------+

Rationale

This pattern results in 4.5 RTT for a PTLC forward.

Single-sig adaptor, sync, with ANYPREVOUT

    +-------+                                 +-------+
    |       |                                 |       | Alice's turn
    |       |--(1)---- update_offer_ptlc ---->|       | new amounts, lock info, a_o_ptlc (unbound adaptor APO sig for {a,b}tx)
    |       |--(2)---- update_offer_ptlc ---->|       | new amounts, lock info, a_o_ptlc (unbound adaptor APO sig for {a,b}tx)
    |       |--(3)--- commitment_signed ----->|       | Alice's B_commit sig
    |       |--(4)--- b_o_btx_sign----------->|       | Alice's full sig for (d) PTLC-timeouts back to Bob
    |       |                                 |       |
    |   A   |<-(5)--- revoke_and_ack ---------|   B   | Bob can broadcast latest state safely, revoke older
    |       |<-(6)-- commitment_signed -------|       | Bob's A_commit sig
    |       |<-(7)---a_o_atx_sign-------------|       | Bob's full sig for (a) PTLC-timeouts back to Alice
    |       |                                 |       |
    |       |--(8)--- revoke_and_ack -------->|       | Alice is now committed, Bob can now safely forward
    |       |                                 |       |
    +-------+                                 +-------+

Rationale

Since we don't ever have to predict commitment transaction txids, we can send PTLC adaptor signatures inside the PTLC offers themselves, and never have to send them again. This results in a much closer message pattern to today's.

Asynchronous should be the same.

Single-sig adaptor, ln-symmetry

This one is just for fun.

    +-------+                                 +-------+
    |       |                                 |       | Alice's turn
    |       |--(1)---- update_offer_ptlc ---->|       | new amounts, lock info, a_o_ptlc (unbound adaptor APO sig for PTLC point)
    |   A   |--(2)---- update_offer_ptlc ---->|   B   | new amounts, lock info, a_o_ptlc (unbound adaptor APO sig for PTLC point)
    |       |--(3)--- update_signed --------->|       | Alice's psig for update tx. Bob can now forward
    |       |                                 |       |
    +-------+                                 +-------+

Rationale

Uses simplified updates. A single partial signature per state update, and a single adaptor signature per added PTLC.

T-bast reordering updates

Forgot about t-bast's ideas which details a reordering that can save round-trips.

Single-sig adaptor, sync updates

+-------+                                 +-------+
|       |                                 |       | Alice's turn
|       |--(1)---- update_offer_ptlc ---->|       | new amounts, lock info
|       |--(2)---- update_offer_ptlc ---->|       | new amounts, lock info
|       |--(3)--- a_o_atx_presign-------->|       | Alice re-commits to all (a) PTLC-S paths with adaptor sigs
|       |                                 |       |
|       |<-(4)-- commitment_signed -------|       | Bob's A_commit sig
|       |<-(5)---b_o_atx_presign----------|       | Bob's adaptor signatures for (c)
|       |<-(6)---a_o_atx_sign-------------|       | Bob's full sig for (a) PTLC-timeouts back to Alice
|       |<-(7)--- b_o_btx_presign---------|       | Bob re-commits to all (d) PTLC-Success paths (must be before commit)
|       |                                 |       | 
|       |--(8)--- revoke_and_ack -------->|       | Alice is now committed, Bob can now safely forward
|       |--(9)--- commitment_signed ----->|       | Alice's B_commit sig
|       |--(10)-- b_o_btx_sign----------->|       | Alice's full sig for (d) PTLC-timeouts back to Bob
|       |--(12)-- a_o_btx_presign-------->|       | Alice's adaptor sig for (b)
|       |                                 |       |
|   A   |<-(13)-- revoke_and_ack ---------|   B   | Bob can broadcast latest state safely, revoke older
|       |                                 |       |
|       |                                 |       |
+-------+                                 +-------+

Rationale:

If we have Bob commit first, this cuts out round-trips. Same effect below.

MuSig adaptor, sync updates

+-------+                                 +-------+ Alice's turn
|       |                                 |       |
|       |--(1)---- update_offer_ptlc ---->|       | new amounts, lock info
|       |--(2)---- update_offer_ptlc ---->|       | new amounts, lock info
|       |--(3)--- b_o_atx_psig----------->|       | Alice psigning Bob-offered PTLC in Alice tx (c)  (Bob can now safely send commitment_signed)
|       |--(4)---- b_o_btx_psig---------->|       | Alice psigning Bob-offered PTLC in Bob tx (d)        
|       |                                 |       |
|       |<-(5)----b_o_atx_psig------------|       | Bob psigning Bob-offered PTLC in Alice tx (c)   
|       |<-(6)--- b_o_btx_psig------------|       | Bob re-commits to all (d) PTLC-Success paths (must be before commit)
|       |<-(8)--- a_o_atx_psig------------|       | Bob psigning Alice-offered PTLC in Alice tx (a)
|       |<-(9)--- a_o_btx_psig------------|       | Bob psigning Alice-offered PTLC in Bob tx (b)         
|       |<-(7)--- commitment_signed ------|       | Alice now knows Bob's sigs for Alice's commit tx
|       |                                 |       |
|       |--(10)--- revoke_and_ack ------->|       | Alice still needs to commit to the new PTLCs before Bob can forward
|       |--(11)-- commitment_signed ----->|       | Bob knows full local commit tx sig for Alice (and Alice knows PTLC-S (d)) 
|       |--(12)-- a_o_atx_psig----------->|       | Alice psigning Alice-offered PTLC in Alice tx (a)
|       |--(13)-- a_o_btx_psig----------->|       | Alice psigning Alice-offered PTLC in Bob tx (b)
|       |                                 |       |   Alice is now committed, Bob can now safely forward
|   A   |<-(14)---revoke_and_ack ---------|   B   | All Alice-offered PTLCs locked in, new tx safe for Bob
|       |                                 |       |
+-------+                                 +-------+

Conclusions & Questions

  1. With a bit of message re-ordering and sync updates, we can keep 1.5RTT without too much pain
    1. async updates likely possible as well, but makes my head hurt more than it already does
  2. Otherwise, PTLCs, in the absense of deeper commitment transaction updates ala "fast-forward", add some forwarding latency
    1. Async updates paradoxically add PTLC forwarding latency. Is this the time to adopt a simplified update protocol? Would async updates make fast-forwarding impossible?
    2. MuSig2 adds a round-trip of latency in many cases, even with "fast-forwards", or ANYPREVOUT based constructs like ln-symmetry. It also adds nonce management per-commitment tx, per-PTLC.
  3. Absent total mempool re-writes, the cost of MuSig2 or single-signature adaptors will end up being the same due to bring-your-own-fee requirements. It seems to have no upsides currently, and introduces nonce management.
  4. ANYPREVOUT makes things a lot less convoluted, even for penalty based channels. One MuSig2(and pair of nonces) or adaptor signature per PTLC, lifetime.
  5. Would inversion of who sends commitment_signed first interfere with other updates on simplified update mechanism?
  6. Single-sig adaptors are not widely adopted compared to MuSig2 variant. There is a single unmereged implementation that needs review and standardization.

Some reasonable choices:

  1. single-sig adaptor, async updates, no message re-ordering. Highest number of round-trips to forward (3.5RTT)
  2. single-sig adaptor, sync updates, no message re-ordering. Slightly fewer round-trips than above to forward (2.5RTT)
  3. single-sig adaptor, sync updates, switch up commitment ordering. Same forwarding round-trips as today (1.5RTT)
    1. can be swapped out with MuSig2 upon future mempool architecture with replace-by-feerate?
  4. "fast-forward" commitment transaction rewrite, sync updates(?), which was deemed Future Work prior (0.5RTT)
@hieblmi
Copy link

hieblmi commented Oct 18, 2023

Shouldn't If Alice sends commitment_signed (5) without (3) being received, ... change to If Alice sends commitment_signed (5) without (4) being received, ...?

@instagibbs
Copy link
Author

@hieblmi thanks fixed

@editor-Ajian
Copy link

editor-Ajian commented Jan 22, 2025

This comment is mainly for single-sig adaptor, MuSig2 adaptor and reordering updates.


I think, adding more terms for signatures and transactions being signed is better. For example, for the local party (Bob), depends on the implementation of PTLCs (Single-sig or MuSig2), there are four kinds of signatures:

  • for received PTLCs, signatures for PTLC-Success,
    • the local party pass to the remote party in advance, only exist in MuSig2 Adaptor,
    • let's call it fair_signature (as it is for ensuring the remote is able to extract secret in the aggregated signatures)
  • for offered PTLCs, signatures for Claim-PTLC-Success (to be consistent with t-bast's term 1), a.k.a b_o_btx,
    • the local party pass to the remote party in advance,
    • setting up the point lock condition for the remote
  • for received PTLCs, signatures for PTLC-Success,
    • the remote party pass to the local party
  • for offered, signatures for PTLC-Timeout,
    • the remote party pass to the local party

Based on that, I think, the count in the 'Single-sig adaptors, sync updates' section is optimistic, or unfair, as Bob do not propose updates (but in 'Single-sig adaptors, async updates' section, he does).

If we let Bob send an update_offer_ptlc,Alice have to send a_o_atx_presign in a separated round-trip, so the result is 3.5 RTT, which is the same as async mode.

The reason seems to be, the transmission need of signatures for Claim-PTLC-Success and thus done-like message (explicitly requiring the peer to stop proposing updates), alleviates the differences between sync mode and async mode.


Here, I don't understand why Alice have to send b_o_btx_psig at first in 'MuSig2-based adaptor signatures' sections. Does it mean you use MuSig2 to execute point lock condition in b_o_btx(sign for Claim-PTLC-Success)? I think it is very complicated in nonce management (we need two musig2 aggregated nonces for one offered PTLCs in commitments being signed.)

If we use a 2-of-2 script and single-sig adaptor instead, (yes, it becomes a mix method), the interaction will be more clear (in sync mode):

    +-------+                                 +-------+ Alice's turn
    |       |                                 |       |
    |       |--(1)---- update_offer_ptlc ---->|       | new amounts, lock info
    |       |--(2)---- update_offer_ptlc ---->|       | new amounts, lock info
    |       |--(3)---- done ----------------->|       | Prompt Bob to send (d) adaptor sigs and (b) fair sigs
    |       |                                 |       |
    |       |<-(4)--- b_o_btx_sig ------------|       | Bob signed all Claim-PTLC-Success (must be before commit)
    |       |<-(5)--- a_o_btx_psig -----------|       | Bob psigning Alice-offered PTLC in Bob tx (b)
    |       |                                 |       |
    |       |--(6)--- commitment_signed ----->|       | Bob knows full local commit tx sig for Alice (and Alice knows PTLC-S (d))
    |       |--(7)--- b_o_btx_psig ---------->|       | Alice psigning Bob-offered PTLC Bob tx (d) (PTLC-Timeout)
    |       |--(8)-- a_o_btx_psig ----------->|       | Alice psigning Alice-offered PTLC in Bob tx (b) (PTLC-Success)
    |       |                                 |       |
    |       |<-(9)---revoke_and_ack ----------|       | All Alice-offered PTLCs locked in, new tx safe for Bob
    |       |<-(10)-- update_offer_ptlc ------|       | new amounts, lock info 
    |       |<-(11)-- done -------------------|       | Prompt Alice to send (a) adaptor sigs and (c) fair sigs
    |       |                                 |       |
    |       |--(12)---  a_o_atx_sig --------->|       | Alice signed all Claim-PTLC-Success
    |       |--(13)---  b_o_atx_psig -------->|       | Alice partial signed PLTC-Suceess for (c) (fair sigs)
    |       |                                 |       |
    |       |<-(14)-- commitment_signed ------|       | Alice now knows Bob's sigs for Alice's commit tx
    |       |                                 |       | Bob wasn't allowed to add his own PTLCs so Alice can finish up
    |       |<-(15)---  a_o_atx_psig ---------|       | Bob partial signed PTLC-Timeout for (a)
    |       |<-(16)---  b_o_atx_psig ---------|       | Bob partial signed PLTC-Suceess for (c)
    |       |                                 |       |
    |       |--(17)--- revoke_and_ack ------->|       | Alice is now committed, Bob can now safely forward
    |       |                                 |       |
    +-------+                                 +-------+

The result is 4 RTT, but we let Bob propose an update.


We may say, the definition of sync mode (simple_update) is, at any time (one's turn):

  1. only one party is able to propose updates
  2. only one party is able to stop/shift this turn

Then, sync mode do not conflict with t-bast's 'reordering updates': it just like, in Alice's turn, Alice ask Bob to sign her commitment, but do not stop her turn; rather, the only signal of shifting her turn is her commitment_signed message.

However, this mode lacks a big advantage IMO:

  1. only one party's commitment would be signed

In other words, combined (1)(2)(3) become an extreme simplification for LN implementation in current spec. Combined (1)(2) is just simplification at a discount.

IMO, the 'reordering updates' is similar with the method you leverage here in a way: let Bob speed up join into signing stage (sign for Alice, the one proposing update) to save round-trip. However, if Bob is holding updates, it will cost more round-trip.

By contrast, the idea of original simple_update is, making every proposing have a start and an end, simplifying while preventing waste.


I also think MuSig2 Adaptor is unreasonable as it cannot use SIGHASH_SINGLE | ANYONECANPAY. But sync mode seems to be better than async update and 'reordering' is only suitable for async update.

Footnotes

  1. https://github.com/t-bast/lightning-docs/blob/master/taproot-updates.md#point-time-locked-contracts

@instagibbs
Copy link
Author

Bob do not propose updates (but in 'Single-sig adaptors, async updates' section, he does).

It's been a very long time since I've looked at this document, but the whole point of that exercise is to show that given the case where we allow async updates, the overall protocol gets more complex as it must be handled properly. I'm not sure I'd call that "unfair".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment