Skip to content

Instantly share code, notes, and snippets.

@t-bast
Last active October 2, 2023 13:57
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save t-bast/1ac31f4e27734a10c5b9847d06db8d86 to your computer and use it in GitHub Desktop.
Save t-bast/1ac31f4e27734a10c5b9847d06db8d86 to your computer and use it in GitHub Desktop.
Alternative splicing specification proposal with detailed examples

Splice specification proposal

This gist details various splicing protocol flows as they are currently implemented in eclair. We detail the exact flow of messages for each scenario, which should help the review process.

Terminology

We call "active commitments" the set of valid commitment transactions to which updates (update_add_htlc, update_fulfill_htlc, update_fail_htlc, update_fail_malformed_htlc, update_fee) must be applied. While a funding transaction is unconfirmed, updates must be valid for all active commitments.

When representing active commitments, we will only draw the corresponding funding transactions for simplicity. The related commitment transaction simply spends that funding transaction.

For example, the following diagram displays the active commitments when we have an unconfirmed splice (FundingTx2a) and 2 RBF attempts for that splice (FundingTx2b and FundingTx2c). We thus have 4 active commitments:

  • the commitment spending FundingTx1
  • the commitments spending each splice transaction (FundingTx2a, FundingTx2b and FundingTx2c)
+------------+                +-------------+
| FundingTx1 |--------+------>| FundingTx2a |
+------------+        |       +-------------+
                      |
                      |       +-------------+
                      +------>| FundingTx2b |
                      |       +-------------+
                      |
                      |       +-------------+
                      +------>| FundingTx2c |
                              +-------------+

Peers must always agree on the set of active commitments, otherwise one side will expect signatures that the other side will not send, which will lead to force-closing the channel.

Added fields to existing lightning messages

We add a few fields to existing lightning messages to make splicing work.

commit_sig

When a splice is ongoing, we must send one commit_sig per active commitment. We add a batch_size field to commit_sig to let the receiver know that they should wait for additional commit_sig messages. We send the batch of commit_sig messages in the order in which the corresponding funding transactions have been created, this way we can implicitly match a commit_sig to its corresponding funding transaction without any additional data.

channel_reestablish

We require nodes to store the funding transaction as soon as they send their commit_sig. Note that this requires storing a transaction before we have the remote commit_sig, and thus before we can create the corresponding local commitment transaction. This is necessary to handle disconnections happening in the middle of the signatures exchange, as will be shown in protocol flows below.

We add a next_funding_txid field to the channel_reestablish message, that must be set whenever a node has sent commit_sig (and thus stored the funding transaction) but has not received commit_sig or tx_signatures from the remote node.

When a node receives a channel_reestablish message containing next_funding_txid:

  • If next_funding_txid does not match the last funding transaction:
    • The receiving node must send tx_abort to let the remote node know that they can safely forget this funding attempt
    • This only happens if the receiving node has not stored the funding transaction, which means it has not sent its commit_sig for that funding transaction
    • Consequently, the remote node has not sent its tx_signatures for that funding transaction
    • This is why it is safe for the remote node to forget the funding attempt when receiving tx_abort
    • But the remote node must ignore tx_abort if it had already sent its tx_signatures (which only happens if the receiving node is malicious, as explained above)
    • This protocol flow is detailed below
  • If next_funding_txid matches the last funding transaction:
    • The receiving node must retransmit its commit_sig for that funding transaction
    • If the receiving node is supposed to send tx_signatures first or if it has already received the remote tx_signatures:
      • The receiving node must retransmit its tx_signatures
    • Otherwise it must wait for the remote tx_signatures before sending its own tx_signatures
    • These protocol flows are detailed in the samples below

A very important detail to understand about the reestablish scenarios is that whenever next_funding_txid is set, it means we got disconnected before both peers received tx_signatures. Consequently, we were in stfu mode when that disconnection happened (for at least one of the two peers), which means that commitments are in sync (both peers are using the same commitment_number). This is important, because it means that we can't have complex interactions between "standard" reestablish scenarios and "unsigned splice" reestablish scenarios. At worst, we can have one side that wasn't in stfu mode anymore and sent updates that weren't received and need to be retransmitted on reconnection, but this is trivial to handle and doesn't require any implementation update, as detailed in this protocol flow.

Sample protocol flows

In the protocol flows below, we omit the interactive-tx messages that build the transaction. The only interactive-tx messages we explicitly list are the consecutive tx_complete that mark the end of the interactive-tx construction.

Successful single splice

Let's warm up with the simplest possible flow: a splice transaction that confirms without any disconnection.

Initial active commitments:

   commitment_number = 10
   +------------+
   | FundingTx1 |
   +------------+

Alice initiates a splice:

   Alice                           Bob
     |             stfu             |
     |----------------------------->|
     |             stfu             |
     |<-----------------------------|
     |          splice_init         |
     |----------------------------->|
     |          splice_ack          |
     |<-----------------------------|
     |                              |
     |       <interactive-tx>       |
     |<---------------------------->|
     |                              |
     |         tx_complete          |
     |----------------------------->|
     |         tx_complete          |
     |<-----------------------------|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |----------------------------->|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |<-----------------------------|
     |        tx_signatures         |
     |----------------------------->|
     |        tx_signatures         |
     |<-----------------------------|
     |                              | The channel is no longer quiescent at that point.
     |                              | Active commitments:
     |                              | 
     |                              |    commitment_number = 10
     |                              |    +------------+        +------------+
     |                              |    | FundingTx1 |------->| FundingTx2 |
     |                              |    +------------+        +------------+
     |                              |
     |       update_add_htlc        | Alice and Bob use the channel while the splice transaction is unconfirmed.
     |----------------------------->|
     |       update_add_htlc        |
     |----------------------------->|
     |         commit_sig           | batch_size = 2, funding_txid = FundingTx1, commitment_number = 11
     |----------------------------->|
     |         commit_sig           | batch_size = 2, funding_txid = FundingTx2, commitment_number = 11
     |----------------------------->|
     |       revoke_and_ack         |
     |<-----------------------------|
     |         commit_sig           | batch_size = 2, funding_txid = FundingTx1, commitment_number = 11
     |<-----------------------------|
     |         commit_sig           | batch_size = 2, funding_txid = FundingTx2, commitment_number = 11
     |<-----------------------------|
     |       revoke_and_ack         |
     |----------------------------->|
     |                              | Active commitments:
     |                              | 
     |                              |    commitment_number = 11
     |                              |    +------------+        +------------+
     |                              |    | FundingTx1 |------->| FundingTx2 |
     |                              |    +------------+        +------------+
     |                              |
     |        splice_locked         | The splice transaction confirms.
     |----------------------------->|
     |        splice_locked         |
     |<-----------------------------|
     |                              | Active commitments:
     |                              | 
     |                              |    commitment_number = 11
     |                              |    +------------+
     |                              |    | FundingTx2 |
     |                              |    +------------+
     |                              | 
     |       update_add_htlc        | Alice and Bob can use the channel and forget the previous FundingTx1.
     |----------------------------->|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 12
     |----------------------------->|
     |       revoke_and_ack         |
     |<-----------------------------|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 12
     |<-----------------------------|
     |       revoke_and_ack         |
     |----------------------------->|
     |                              | Active commitments:
     |                              | 
     |                              |    commitment_number = 12
     |                              |    +------------+
     |                              |    | FundingTx2 |
     |                              |    +------------+

Multiple splices with racy splice_locked

Since nodes have different views of the blockchain, they may send splice_locked at slightly different times. Moreover, nodes may send splice_locked concurrently with other channel updates, in which case they will receive some commit_sig messages for obsolete commitments. This is fine: nodes know how many commit_sig messages to expect thanks to the batch_size field, and they can simply ignore commit_sig messages for which the funding_txid cannot be found in the active commitments.

Initial active commitments:

   commitment_number = 10
   +------------+
   | FundingTx1 |
   +------------+

Alice initiates a splice:

   Alice                           Bob
     |             stfu             |
     |----------------------------->|
     |             stfu             |
     |<-----------------------------|
     |          splice_init         |
     |----------------------------->|
     |          splice_ack          |
     |<-----------------------------|
     |                              |
     |       <interactive-tx>       |
     |<---------------------------->|
     |                              |
     |         tx_complete          |
     |----------------------------->|
     |         tx_complete          |
     |<-----------------------------|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2a, commitment_number = 10
     |----------------------------->|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2a, commitment_number = 10
     |<-----------------------------|
     |        tx_signatures         |
     |----------------------------->|
     |        tx_signatures         |
     |<-----------------------------|
     |                              | Active commitments:
     |                              | 
     |                              |    commitment_number = 10
     |                              |    +------------+        +-------------+
     |                              |    | FundingTx1 |------->| FundingTx2a |
     |                              |    +------------+        +-------------+
     |                              |
     |             stfu             |
     |----------------------------->|
     |             stfu             |
     |<-----------------------------|
     |          tx_init_rbf         | Alice RBFs the splice attempt.
     |----------------------------->|
     |          tx_ack_rbf          |
     |<-----------------------------|
     |                              |
     |       <interactive-tx>       |
     |<---------------------------->|
     |                              |
     |         tx_complete          |
     |----------------------------->|
     |         tx_complete          |
     |<-----------------------------|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2b, commitment_number = 10
     |----------------------------->|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2b, commitment_number = 10
     |<-----------------------------|
     |        tx_signatures         |
     |----------------------------->|
     |        tx_signatures         |
     |<-----------------------------|
     |                              | Active commitments:
     |                              | 
     |                              |    commitment_number = 10
     |                              |    +------------+        +-------------+
     |                              |    | FundingTx1 |---+--->| FundingTx2a |
     |                              |    +------------+   |    +-------------+
     |                              |                     |
     |                              |                     |    +-------------+
     |                              |                     +--->| FundingTx2b |
     |                              |                          +-------------+
     |                              |
     |       update_add_htlc        | Alice and Bob use the channel while the splice transactions are unconfirmed.
     |----------------------------->|
     |       update_add_htlc        |
     |----------------------------->|
     |         commit_sig           | batch_size = 3, funding_txid = FundingTx1, commitment_number = 11
     |----------------------------->|
     |         commit_sig           | batch_size = 3, funding_txid = FundingTx2a, commitment_number = 11
     |----------------------------->|
     |         commit_sig           | batch_size = 3, funding_txid = FundingTx2b, commitment_number = 11
     |----------------------------->|
     |       revoke_and_ack         |
     |<-----------------------------|
     |         commit_sig           | batch_size = 3, funding_txid = FundingTx1, commitment_number = 11
     |<-----------------------------|
     |         commit_sig           | batch_size = 3, funding_txid = FundingTx2a, commitment_number = 11
     |<-----------------------------|
     |         commit_sig           | batch_size = 3, funding_txid = FundingTx2b, commitment_number = 11
     |<-----------------------------|
     |       revoke_and_ack         |
     |----------------------------->|
     |                              | Active commitments:
     |                              | 
     |                              |    commitment_number = 11
     |                              |    +------------+        +-------------+
     |                              |    | FundingTx1 |---+--->| FundingTx2a |
     |                              |    +------------+   |    +-------------+
     |                              |                     |
     |                              |                     |    +-------------+
     |                              |                     +--->| FundingTx2b |
     |                              |                          +-------------+
     |                              |
     |        splice_locked         | funding_txid = FundingTx2b
     |----------------------------->|
     |       update_add_htlc        |
     |----------------------------->|
     |         commit_sig           | batch_size = 3, funding_txid = FundingTx1, commitment_number = 12 -> this message can be ignored by Bob since FundingTx2b will be locked before the end of the batch
     |----------------------------->|
     |        splice_locked         | funding_txid = FundingTx2b
     |<-----------------------------|
     |         commit_sig           | batch_size = 3, funding_txid = FundingTx2a, commitment_number = 12 -> this message can be ignored by Bob since FundingTx2b has been locked
     |----------------------------->|
     |         commit_sig           | batch_size = 3, funding_txid = FundingTx2b, commitment_number = 12
     |----------------------------->|
     |       revoke_and_ack         |
     |<-----------------------------|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2b, commitment_number = 12
     |<-----------------------------|
     |       revoke_and_ack         |
     |----------------------------->|
     |                              | Active commitments:
     |                              | 
     |                              |    commitment_number = 12
     |                              |    +-------------+
     |                              |    | FundingTx2b |
     |                              |    +-------------+

Disconnection with one side sending commit_sig

In this scenario, a disconnection happens when one side has sent commit_sig but not the other.

Initial active commitments:

   commitment_number = 10
   +------------+
   | FundingTx1 |
   +------------+

Alice initiates a splice, but disconnects before receiving Bob's tx_complete:

   Alice                           Bob
     |             stfu             |
     |----------------------------->|
     |             stfu             |
     |<-----------------------------|
     |          splice_init         |
     |----------------------------->|
     |          splice_ack          |
     |<-----------------------------|
     |                              |
     |       <interactive-tx>       |
     |<---------------------------->|
     |                              |
     |         tx_complete          |
     |----------------------------->|
     |         tx_complete          |
     |       X----------------------|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |       X----------------------|
     |                              | Active commitments for Alice:
     |                              | 
     |                              |    commitment_number = 10
     |                              |    +------------+
     |                              |    | FundingTx1 |
     |                              |    +------------+
     |                              | 
     |                              | Active commitments for Bob:
     |                              | 
     |                              |    commitment_number = 10
     |                              |    +------------+        +------------+
     |                              |    | FundingTx1 |------->| FundingTx2 |
     |                              |    +------------+        +------------+
     |                              |
     |      channel_reestablish     | next_funding_txid = null, next_commitment_number = 11, next_revocation_number = 10
     |----------------------------->|
     |      channel_reestablish     | next_funding_txid = FundingTx2, next_commitment_number = 11, next_revocation_number = 10
     |<-----------------------------|
     |           tx_abort           |
     |----------------------------->|
     |           tx_abort           |
     |<-----------------------------|
     |                              | Bob can safely forget the splice attempt because he hasn't sent tx_signatures.
     |                              | Active commitments for Alice and Bob:
     |                              | 
     |                              |    commitment_number = 10
     |                              |    +------------+
     |                              |    | FundingTx1 |
     |                              |    +------------+

Disconnection with both sides sending commit_sig

In this scenario, a disconnection happens when both sides have sent commit_sig. They are able to resume the signatures exchange on reconnection. In this example, Bob is supposed to send tx_signatures first.

Initial active commitments:

   commitment_number = 10
   +------------+
   | FundingTx1 |
   +------------+

Alice initiates a splice, but disconnects before receiving Bob's commit_sig:

   Alice                           Bob
     |             stfu             |
     |----------------------------->|
     |             stfu             |
     |<-----------------------------|
     |          splice_init         |
     |----------------------------->|
     |          splice_ack          |
     |<-----------------------------|
     |                              |
     |       <interactive-tx>       |
     |<---------------------------->|
     |                              |
     |         tx_complete          |
     |----------------------------->|
     |         tx_complete          |
     |<-----------------------------|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |--------------------X         |
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |       X----------------------|
     |                              | Active commitments:
     |                              | 
     |                              |    commitment_number = 10
     |                              |    +------------+        +------------+
     |                              |    | FundingTx1 |------->| FundingTx2 |
     |                              |    +------------+        +------------+
     |                              |
     |      channel_reestablish     | next_funding_txid = FundingTx2, next_commitment_number = 11, next_revocation_number = 10
     |----------------------------->|
     |      channel_reestablish     | next_funding_txid = FundingTx2, next_commitment_number = 11, next_revocation_number = 10
     |<-----------------------------|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |----------------------------->|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |<-----------------------------|
     |        tx_signatures         |
     |<-----------------------------|
     |        tx_signatures         |
     |----------------------------->|
     |                              | Active commitments:
     |                              | 
     |                              |    commitment_number = 10
     |                              |    +------------+        +------------+
     |                              |    | FundingTx1 |------->| FundingTx2 |
     |                              |    +------------+        +------------+

Disconnection with one side sending tx_signatures

In this scenario, a disconnection happens when one side has sent tx_signatures but not the other. They are able to resume the signatures exchange on reconnection.

Initial active commitments:

   commitment_number = 10
   +------------+
   | FundingTx1 |
   +------------+

Alice initiates a splice, but disconnects before receiving Bob's tx_signatures:

   Alice                           Bob
     |             stfu             |
     |----------------------------->|
     |             stfu             |
     |<-----------------------------|
     |          splice_init         |
     |----------------------------->|
     |          splice_ack          |
     |<-----------------------------|
     |                              |
     |       <interactive-tx>       |
     |<---------------------------->|
     |                              |
     |         tx_complete          |
     |----------------------------->|
     |         tx_complete          |
     |<-----------------------------|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |----------------------------->|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |<-----------------------------|
     |        tx_signatures         |
     |       X----------------------|
     |                              | Active commitments:
     |                              | 
     |                              |    commitment_number = 10
     |                              |    +------------+        +------------+
     |                              |    | FundingTx1 |------->| FundingTx2 |
     |                              |    +------------+        +------------+
     |                              |
     |      channel_reestablish     | next_funding_txid = FundingTx2, next_commitment_number = 11, next_revocation_number = 10
     |----------------------------->|
     |      channel_reestablish     | next_funding_txid = FundingTx2, next_commitment_number = 11, next_revocation_number = 10
     |<-----------------------------|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |----------------------------->|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |<-----------------------------|
     |        tx_signatures         |
     |<-----------------------------|
     |        tx_signatures         |
     |----------------------------->|
     |                              | Active commitments:
     |                              | 
     |                              |    commitment_number = 10
     |                              |    +------------+        +------------+
     |                              |    | FundingTx1 |------->| FundingTx2 |
     |                              |    +------------+        +------------+

Disconnection with both sides sending tx_signatures

In this scenario, a disconnection happens when both sides have sent tx_signatures, but one side has not received it. They are able to resume the signatures exchange on reconnection.

Initial active commitments:

   commitment_number = 10
   +------------+
   | FundingTx1 |
   +------------+

Alice initiates a splice, but disconnects before Bob receives her tx_signatures:

   Alice                           Bob
     |             stfu             |
     |----------------------------->|
     |             stfu             |
     |<-----------------------------|
     |          splice_init         |
     |----------------------------->|
     |          splice_ack          |
     |<-----------------------------|
     |                              |
     |       <interactive-tx>       |
     |<---------------------------->|
     |                              |
     |         tx_complete          |
     |----------------------------->|
     |         tx_complete          |
     |<-----------------------------|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |----------------------------->|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |<-----------------------------|
     |        tx_signatures         |
     |<-----------------------------|
     |        tx_signatures         |
     |----------------------X       |
     |                              | Active commitments:
     |                              | 
     |                              |    commitment_number = 10
     |                              |    +------------+        +------------+
     |                              |    | FundingTx1 |------->| FundingTx2 |
     |                              |    +------------+        +------------+
     |                              |
     |      channel_reestablish     | next_funding_txid = null, next_commitment_number = 11, next_revocation_number = 10
     |----------------------------->|
     |      channel_reestablish     | next_funding_txid = FundingTx2, next_commitment_number = 11, next_revocation_number = 10
     |<-----------------------------|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |----------------------------->|
     |        tx_signatures         |
     |----------------------------->|
     |                              | Active commitments:
     |                              | 
     |                              |    commitment_number = 10
     |                              |    +------------+        +------------+
     |                              |    | FundingTx1 |------->| FundingTx2 |
     |                              |    +------------+        +------------+

Disconnection with both sides sending tx_signatures and channel updates

In this scenario, a disconnection happens when both sides have sent tx_signatures, but one side has not received it. The second signer also sent a new signature for additional changes to apply after their tx_signatures. They are able to resume the signatures exchange on reconnection and retransmit new updates.

Initial active commitments:

   commitment_number = 10
   +------------+
   | FundingTx1 |
   +------------+

Alice initiates a splice, but disconnects before Bob receives her tx_signatures and new updates:

   Alice                           Bob
     |             stfu             |
     |----------------------------->|
     |             stfu             |
     |<-----------------------------|
     |          splice_init         |
     |----------------------------->|
     |          splice_ack          |
     |<-----------------------------|
     |                              |
     |       <interactive-tx>       |
     |<---------------------------->|
     |                              |
     |         tx_complete          |
     |----------------------------->|
     |         tx_complete          |
     |<-----------------------------|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |----------------------------->|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |<-----------------------------|
     |        tx_signatures         |
     |<-----------------------------|
     |        tx_signatures         |
     |----------------------X       |
     |       update_add_htlc        |
     |----------------------X       |
     |         commit_sig           | batch_size = 2, funding_txid = FundingTx1, commitment_number = 11
     |----------------------X       |
     |         commit_sig           | batch_size = 2, funding_txid = FundingTx2, commitment_number = 11
     |----------------------X       |
     |                              | Active commitments:
     |                              | 
     |                              |    commitment_number = 10
     |                              |    +------------+        +------------+
     |                              |    | FundingTx1 |------->| FundingTx2 |
     |                              |    +------------+        +------------+
     |                              |
     |      channel_reestablish     | next_funding_txid = null, next_commitment_number = 11, next_revocation_number = 10
     |----------------------------->|
     |      channel_reestablish     | next_funding_txid = FundingTx2, next_commitment_number = 11, next_revocation_number = 10
     |<-----------------------------|
     |         commit_sig           | batch_size = 1, funding_txid = FundingTx2, commitment_number = 10
     |----------------------------->|
     |        tx_signatures         |
     |----------------------------->|
     |       update_add_htlc        |
     |----------------------------->|
     |         commit_sig           | batch_size = 2, funding_txid = FundingTx1, commitment_number = 11
     |----------------------------->|
     |         commit_sig           | batch_size = 2, funding_txid = FundingTx2, commitment_number = 11
     |----------------------------->|
     |       revoke_and_ack         |
     |<-----------------------------|
     |         commit_sig           | batch_size = 2, funding_txid = FundingTx1, commitment_number = 11
     |<-----------------------------|
     |         commit_sig           | batch_size = 2, funding_txid = FundingTx2, commitment_number = 11
     |<-----------------------------|
     |       revoke_and_ack         |
     |----------------------------->|
     |                              | Active commitments:
     |                              | 
     |                              |    commitment_number = 11
     |                              |    +------------+        +------------+
     |                              |    | FundingTx1 |------->| FundingTx2 |
     |                              |    +------------+        +------------+
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment