Skip to content

Instantly share code, notes, and snippets.

@t-bast
Created May 12, 2021 17:21
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save t-bast/a7779d82cd1ccf6c99f610d05fc7bc2f to your computer and use it in GitHub Desktop.
Save t-bast/a7779d82cd1ccf6c99f610d05fc7bc2f to your computer and use it in GitHub Desktop.
Phoenix in a splicing future (a.k.a death to all swaps)

Phoenix in a splicing future (a.k.a death to all swaps)

Phoenix implements trusted swaps for users' convenience (to allow easy onboarding and offboarding). But it's not a satisfying solution for the following reasons:

  • It uses two on-chain transactions for swap-in where one should be sufficient (one transaction from the user to Acinq followed by a channel open)
  • Swap-out feerates are unpredictable (because we may fund the swap-out with unconfirmed previous outputs) which is frustrating for users
  • It forces Acinq to use its own utxos, which doesn't scale well and is an operational burden
  • If Acinq doesn't have any utxos available and the mempool is completely full, swaps are stuck which is also frustrating for users

Fortunately, the recent work on dual funding and splicing paves the way for much better solutions. A big thanks to niftynei and rustyrussell for their early work on these protocols.

Getting funds in a channel

Let's imagine that we have an existing 100 mBTC channel and want to add 10 mBTC to it (most likely because we don't have sending liquidity). The flow would look like:

Bitcoin Cold Wallet                        Phoenix                           Acinq
       |                                      |                                |
       |                                      |              <1>               |
       |                                      |      can I haz splice plz?     |
       |                                      |------------------------------->|
       |                                      |<-------------------------------|
       |                 <2>                  |           go for it!           |
       |     can I haz bitcoins? <PSBT>       |                                |
       |<-------------------------------------|                                |
       |------------------------------------->|                                |
       |       here you go: <PSBT>            |                                |
       |                                      |              <3>               |
       |                                      |      can I haz sig plz?        |
       |                                      |------------------------------->|
       |                                      |<-------------------------------|
       |                                      |        <tx_complete>           |
       |                                      |                                |

Here is a more detailed breakdown of each step (fees are omitted for simplicity):

  1. Phoenix tells Acinq that it wants to splice 10 mBTC in the channel. Phoenix creates a PSBT with one input (the channel output in the funding tx) and one 110 mBTC output (the new funding output). This PSBT goes through the creator and updater roles, but has no signatures yet.
  2. Phoenix sends this PSBT to its cold wallet, requesting it to contribute 10 mBTC. The cold wallet adds inputs and optionnally a change output. Then the cold wallet signs its inputs (with SIGHASH_ALL).
  3. Phoenix adds its partial signature for the 2-of-2 funding output to the PSBT and sends it to Acinq. The peer verifies the PSBT and adds its partial signature for the 2-of-2 funding output. It should then be able to finalize the PSBT and broadcast the resulting splice tx.

Voilà, the wallet user has been able to swap some funds in and Acinq didn't have to contribute any utxo. The transactions look like this:

+------------+                +-----------+
| funding tx |-------+------->| commit tx |
+------------+       |        +-----------+
                     |
                     |        +-----------+        +-----------+
                     +------->|           |------->| commit tx |
                              | splice tx |        +-----------+
                     +------->|           |----+
+-------------+      |        +-----------+    | change    +-------------+
| cold wallet |------+                         +---------->| cold wallet |
+-------------+                                            +-------------+

This makes the process much more predictable for wallet users: they decide the feerate and can use coin control in their cold wallet. The UX flow is simple enough as well:

  1. The user clicks Receive -> On-Chain and chooses the amount
  2. A QR code is displayed containing a PSBT
  3. The user scans this QR code from his cold wallet and instructs it to add 10 mBTC
  4. The user obtains a new QR code with the PSBT signed by the cold wallet
  5. The user scans that QR code inside Phoenix and this will complete the flow

Getting funds out of a channel

Getting funds out is even easier. Let's imagine again that we have an existing 100 mBTC channel and want to send 10 mBTC back on-chain. The user simply needs to create a receiving address from his cold wallet, and start a splice that sends 10 mBTC to this address:

+------------+                +-----------+
| funding tx |-------+------->| commit tx |
+------------+       |        +-----------+
                     |
                     |        +-----------+        +-----------+
                     +------->|           |------->| commit tx |
                              | splice tx |        +-----------+
                              |           |----+
                              +-----------+    | 10 mBTC   +-------------+
                                               +---------->| cold wallet |
                                                           +-------------+

Note that this shrinks the capacity of the channel. This can be an issue compared to the legacy swap-out, which didn't change the channel capacity and provided incoming liquidity to the user. But we can add the option to request liquidity (against a fee) and Acinq will in that case add utxos to the splice. We would simply need to use a custom TLV to transfer the fee directly to the initial main outputs of the new commit tx.

Additional goodies

An interesting side-note is that it means we only need one channel (c-lightning was right 😅). It's much better than having multiple channels for a whole lot of reasons:

  • Smaller graph, less gossip
  • Smaller backups
  • More intuitive balance: combined with trampoline, it means we know exactly how much we can send/receive (no MPP surprises)

Note however that there are still reasons to have multiple channels, but they don't apply to wallets:

  • The number of pending HTLCs in a channel is limited; you may need multiple channels for better throughput on congested paths
  • Are there others?

Whenever the user splices some funds in, it can pay some fees to the Acinq node and the Acinq node will also add funds to provide incoming liquidity.

Whenever the user receives an HTLC for which is doesn't have enough receiving liquidity, the Acinq node can splice additional funds in to carry this HTLC. If the user trusts that Acinq will not double-spend this splice, they can consider it complete without confirmation and the HTLC can be relayed. That operation is trusted: a trustless alternative is to hold the HTLC until the splice is confirmed (at the cost of keeping liquidity locked downstream).

Open questions

  • When adding funds, can we avoid choosing the amount before-hand? It would either require the cold wallet to sign with something else than SIGHASH_ALL which is dangerous, or more back-and-forth between the cold wallet and Phoenix which provides a terrible UX...
  • We need to be able to cancel a splice (if the user stops in the middle of the flow or realizes that he doesn't have funds left on his cold wallet): is that supported?
@Kixunil
Copy link

Kixunil commented Aug 31, 2021

I'm not really sure what you're looking for regarding lightning level spec. I was thinking about this protocol for a while and found some more potential for improvement but I don't feel there's much to change in LN spec. Just maybe make extra sure that double spending funding of transaction is not penalized (it can happen if BIP78 fails). Also maybe add the ability to signal time limit (BIP78 mandates one minute timeout) but I don't know if it changes anything.

I also had ideas on paying via BIP78 from splice but that would require changes to the protocol that seem a bit crazy even to me.

Oh, one more thing, BIP78 mandates that inputs/outputs are not reordered only inserted at random positions. So splicing should be made compatible with that, didn't check it that deeply to tell right now.

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