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?
@t-bast
Copy link
Author

t-bast commented Aug 30, 2021

From the proposed protocol it appears PSBT is transferred signed before new commit TX is signed.

No, don't worry, that's not the case, the diagram is a simplification but this will be using dual funding so commit tx will always be signed before we complete the funding tx.

I propose to instead use BIP78 to perform the operation. It fixes all the problems above. It works like this:

You're replacing the splicing protocol by the payjoin protocol, and Acinq would act as a coordinator between Phoenix and the bitcoin cold wallet, is that right?

Does that means the cold wallet needs to support payjoin? If that's the case, it will be unlikely (especially if we want to add an encryption step to avoid Acinq learning anything it shouldn't learn), isn't it? Whereas the current protocol only needs the cold wallet to have PSBT support and all the splicing logic is implemented in Phoenix.

I really want to avoid having to ping every bitcoin wallet maintainer for payjoin support and potential bug fixes. I want this to require the most minimal feature set it needs from bitcoin wallets, otherwise there's no way this will be useful to enough Phoenix users. But once there is wide enough payjoin support in the bitcoin wallet ecosystem we can definitely revisit this (and I urge you to ask bitcoin wallets to implement this support if that's important to you).

@Kixunil
Copy link

Kixunil commented Aug 30, 2021

commit tx will always be signed before we complete the funding tx.

That requires more back-and-forth with PSBTS :(

You're replacing the splicing protocol by the payjoin protocol,

Not replacing splicing, it's replacing manual coordination with automated. The rest is correct.

Does that means the cold wallet needs to support payjoin?

To optimize yes, to work at all no.

it will be unlikely (especially if we want to add an encryption step to avoid Acinq learning anything it shouldn't learn), isn't it?

I'm not really so sure it's unlikely. There are already three wallets supporting it, fourth has a PR open, fifth is planned, I have WIP client with Bitcoind backend so even that is possible. All this just because of privacy. Avoiding two transactions will be much stronger motivation whenever fees are high enough. Wallets without it will become irrelevant.

Remember, there's still fallback to classic address (thanks to BIP21). Anything that doesn't support it will just cause two transactions as today. Contrast that with users having to manually transfer a bunch of PSBTs over humongous QR codes. I bet most users would rather pay more sats. Phoenix is currently regarded as one of the most user-friendly LN wallets if not the most user friendly wallet. I can't imagine this still being the case after one QR code is replaced with several QR codes (users don't care why).

once there is wide enough payjoin support

This is just cursed cycle, there's not enough payjoin support because motivation is low because LN wallets don't implement this because there's not enough support. We gotta break it somewhere and I believe that having a good mobile wallet and a good node app (loptos) implement it to create competitive pressure is more feasible than convincing all developers that if they implement PayJoin then some day LN wallets will use it to optimize fees. Implementing the server is not too hard, I had POC done in a day. Loptos will have big note that best wallets are Wasabi, Blue Wallet and BTCPayServer hot wallet. Any wallet wanting to be on that list will have to implement BIP78.

I urge you to ask bitcoin wallets to implement this support if that's important to you

I'm already doing more than that and intend to do even more. :)

  • Wrote loptos
  • Reported bug to BTCPayServer that got fixed recently
  • In progress of writing Rust client library (should be feasible to create bridges to other langs)
  • Tested PR against Electrum - found and reported a bug

@t-bast
Copy link
Author

t-bast commented Aug 31, 2021

Thanks for insisting on this, this is a lot of interesting information, I didn't think it was getting that much traction.
I'll look into this in detail once we get closer to implementing this feature.

Do you think this could be useful for splicing as whole (at the lightning spec level) or does it only add value for the specific case of having the lightning and bitcoin wallets collaborate?

@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