Submarine swaps that are faster, cheaper, and easier than HTLCs
Papa Swaps are an "upgrade" over regular submarine swaps with the following benefits:
(1) Papa swaps only involve 1 transaction, whereas regular submarine swaps require 2 transactions
(2) Papa swaps are twice as fast as submarine swaps due to needing half the number of transactions
(3) Papa swaps slash the cost of submarine swaps in half due to needing half the number of transactions
(4) Papa swaps slash the cost even further by using taproot, where scripts of this kind are more efficient than they are in segwit v0
Click here and follow the instructions: https://supertestnet.github.io/papa-swap/
A papa swap server does the following things: it (1) sets a swap fee (2) funds a wallet with base layer sats (3) uses an NWC string as a funding source. (I suppose it could use any backend that is capable of receiving lightning payments, but I want to use NWC in my implementation because NWC is cool.) It also listens for swap requests over nostr. When a swap request comes in, it checks that the user wants money equal to or less than the amount in #2. If so, it assesses the fee from #1 and generates a lightning invoice for that amount, which it presents to the user.
If/when the user pays that fee, the server (a) generates another lightning invoice for the amount of the swap (b) treats the preimage to the invoice's payment hash as a private key, and derives its corresponding public key (c) uses the payment hash to construct a smart contract to be described shortly (d) creates two signatures, valid for transactions to be described shortly (e) selects a utxo by which he will fund that smart contract on the base layer with the swap amount and (f) shows the user four things, namely: the lightning invoice from (a), the public key from (b), the utxo data from (e) -- namely, that utxo's txid and vout -- and the two signatures from (d).
The transaction by which the swap service plans to fund the smart contract is called tx0. But it hasn't been broadcasted yet; at this point in the protocol, the user simply has some info from the server. The user, however, needs a few other pieces of info. First, he needs the invoice's payment hash; he can extract that from the invoice from (a), which the server sent him. He also needs to know the smart contract's address, which he can independently construct the same way the swap server did it in (c). He also must verify that the utxo from (e) exists and contains an amount equal to or greater than the amount he wants to swap, and that the two signatures from (d) are valid. Having verified this, the user ought to (g) sign a transaction to be described shortly, (h) share the signature with the server, (i) wait for the server to broadcast tx0 (and for tx0 to confirm), and then (j) pay the lightning invoice from (a), knowing the money in the smart contract is theirs as soon as they do.
[
// tapleaf 1 aka "the first branch"
[ user_pubkey, OP_CHECKSIGVERIFY, server_pubkey, OP_CHECKSIG ],
// tapleaf 2 aka "the second branch"
[ OP_SHA256, payment_hash, OP_EQUALVERIFY, user_pubkey, OP_CHECKSIGVERIFY, server_pubkey, OP_CHECKSIG ],
]
Keypath: user_pubkey tweaked with the pubkey generated by treating payment_preimage as if it were a privkey
Recall that tx0 is the name of the transaction by which the server plans to eventually fund the smart contract. Before the server broadcasts that transaction, the user uses the first branch of the smart contract to sign a transaction called tx1, and shares his signature with the server (he did this at step (g) earlier). Tx1 allows the server to send the money out of the smart contract and into a traditional HTLC with one slight modification. As a reminder of how a traditional HTLC works, its script says the user may take the money if they learn a certain secret (this secret is the same preimage mentioned in (b) above), otherwise the server may take the money after an absolute timelock of 2016 blocks expires. My only modification is, I use a relative timelock instead of an absolute one. The user shares their signature for tx1 with the server.
The server uses the second branch to sign two txs called tx2 and tx3. Both transactions allow the user to use the second branch to withdraw the money from the smart contract -- without waiting -- if they learn the preimage, but tx2 spends the utxo created by tx0 and tx3 spends the utxo created by tx1. The server already shared the signatures for tx2 and tx3 with the user (they were the signatures from (d) earlier), and the user already validated them.
- User requests a submarine swap (lightning -> base layer) and tells the server the amount they want to swap for
- Server calculates a fee and gives a "fee invoice" to the user
- User pays the fee invoice
- Server prepares the swap "smart contract" and a swap invoice, and sends the user the data described in the second paragraph of "How it works"
- User validates the data, signs tx1, and shares their signature with the server
- Server validates the user's signature and funds the smart contract
- User waits for the funding tx to confirm, verifies that the smart contract contains the exact amount needed for the swap (and that the funding utxo is the one they expected), and pays the lightning invoice
- Once the user receives the preimage to the lightning invoice, the protocol is complete: the swap has occurred even though only 1 base layer transaction has happened; the user alone has full control of the money in the smart contract, whereas the swap server alone has full control of the money paid to him or her via lightning
Once the user validates the server’s signatures for tx2 and tx3, the user knows they will be able to withdraw the money from the smart contract via its second branch if they learn the preimage to the swap invoice. They know this because it is a direct result of tx2 and tx3: tx2 lets the user withdraw the money from the smart contract if the user provides the preimage and signatures from themselves and the server. But they already have the server's signature, and the user can create their own signature, so all they need is the preimage, which they get by paying the invoice. So the user does not need to trust the swap service when they pay the swap invoice: as soon as they pay it, they get the preimage, and now they own the money.
After reading that, you may be wondering, "Where does tx3 come in? It sounds like the user only needs tx2." Tx3 comes in due to a race condition: the user can take the money by broadcasting tx2, but if they do, the transaction will go into the mempool, and the server might try to override it by broadcasting tx1 (which spends the same input) and paying a higher fee than the user paid. If that happens, and if the server's transaction (tx1) confirms first, the user's transaction (tx2) -- which tries to spend the same input as tx1 -- will be invalid because bitcoin prevents doublespends from occurring. So that's where tx3 comes in: even if tx1 confirms instead of tx2, all tx1 does is put the money in the htlc, whereupon the server must wait 2016 blocks (2 weeks) to sweep it the "rest" of the way. During that waiting period, the user may broadcast tx3 "first" (because that transaction does not have to wait at all) and thus get their money out that way.
Since the user can get the money out either using tx2 or tx3, the user does not need to trust the server. As soon as they pay the lightning invoice, they have everything they need to spend the money to whatever address they want, whenever they want. Nothing bad can happen. Only 1 on-chain transaction has happened, but money has changed hands on two layers: the server has full control of the money paid to them via the swap invoice, and the user has full control of the money in the smart contract (because tx2 and tx3 let them send it wherever they want regardless of what the server does). It was a submarine swap, but faster and cheaper.
I have said that the user has full, unilateral control of the money in the smart contract as soon as they successfully pay the swap invoice, without the need for a second transaction on the base layer. And I have said that although a race condition can happen if the user and the swap server both broadcast tx1 and tx2 at the same time, this is no big deal, because even if the user loses the race condition, they can still recover their funds via tx3.
But having reached this point, you might be wondering, "What about the htlc? The htlc creates a second race condition. Namely, it lets the server sweep the money after a 2 week period. So, once the user pays the swap invoice, the user does not have full, unilateral control yet; they have to sweep the money as soon as possible, otherwise the server might move the money into the htlc, thus initiating a second race condition: either the user or the server can sweep the money, and which one "wins" is solely determined by miners (and hence, effectively, it goes to whoever pays the highest fee). So you have not really eliminated the need for 2 transactions; you've only delayed the second transaction."
This is false and I'll tell you why: the server cannot sweep the money from the htlc unless (1) they broadcast tx1 first, because tx1 puts the money in the htlc, and (2) after doing that, they wait 2 weeks for the relative timelock to expire. Consequently, there's never a "second" race condition: under the happy path, the swap server never broadcasts tx1, so the 2 week countdown never starts, and the race condition never happens; and under the sad path, the server does broadcast tx1, but the user has 2 weeks to correct that by broadcasting tx3, so there's no race condition in the sad path either.
The trust assumption at the heart of papa swap is equivalent to the one used in the lightning network: the user does not have to trust their counterparty because even if the counterparty tries to cheat (by broadcasting tx1), the user has a lengthy (2 week) opportunity to recover their funds, and during that time there are no race conditions. Therefore, I think this protocol qualifies as giving the user "full, unilateral control" over their money in the same way the lightning network does.
However, since there is a 2 week period involved, I also think papa swaps are best suited for use in self-custodial lightning wallets: those have a similar "check in" period and they have the ability to pay lightning invoices, which aligns well with this protocol because, per steps 3 and 7, the user needs to do that twice.
Although nothing bad can happen to the user if he or she follows the above protocol, there is a way to improve the protocol even further: it is possible to give the user a cheaper way to spend the money in the smart contract. The idea here is this: in taproot, the key path costs less than the script path in terms of mining fees. So it would be great if the user did not have to use the script path, but could use the key path.
For this reason, the user optimistically hopes the preimage given by the server in step 8 of the protocol is also the private key to the public key which, when tweaked with the user’s pubkey, yields the key required for spending the money via the keypath. If it is, then the user can add their own privkey to the preimage to derive the keypath key, and then simply import that key to their wallet, allowing them to spend the money the "cheaper" way whenever they want to.
If, for some reason, the preimage given by the server is not the right privkey, the user should withdraw their money immediately using tx2 because the swap server didn’t follow the protocol correctly, and is therefore some kind of cheater. They can't steal the user's money, but they should probably be penalized anyway for making the user use the more expensive spending path. If, upon seeing the user withdraw their money via tx2, the swap service initiates a race condition by broadcasting tx1, the user should be ready to broadcast tx3 in case tx1 confirms before tx2.
But in the happy path, the preimage does match. And in this case the user can import the keypath key into their wallet and just use the swapped money to pay people whenever it needs to do so. But even in this case the user must also keep hold of tx3 in case the swap service ever broadcasts tx1.
To repeat myself: Voila! Only 1 on-chain transaction has happened, but money has changed hands on two layers: the server has full control of the money paid to them via the swap invoice, and the user has full control of the money in the smart contract (because tx2 and tx3 let them send it wherever they want regardless of what the server does, and the key path lets them spend it without even needing to use the script path). It was a submarine swap, but faster and cheaper.
In the "How it works" section I mentioned that the server must do several things, which I gave numbers to, including #2, which was "[fund] a wallet with base layer sats." Assuming the money in that wallet was not entirely spent in tx0, tx0 will create a change output in the swap service’s wallet, and the swap service can use this change output as a second input to tx1. If the swap service does this, then the user can discard tx3 as soon as that change output is spent, e.g. in another swap, because once that happens, tx1 is invalid and the user knows they will never need tx3 – the user alone controls the funds in the swap address, and the swap server cannot create a race condition by broadcasting tx1, because it has become invalid.
However, this improvement has a small downside: the server must not spend that change output e.g. in another swap until the swap that created the change output is complete. If they do, their own tx1 will be invalid, so the user could hold their money hostage by not paying the swap invoice, whereupon the swap server would have no way to get their money back, since tx1 has been invalidated. I do not think this downside is very big because it's pretty easy to just mark a change output as unspendable for a few blocks. It's unlikely that the server would have no other money to continue performing swaps for other people.
After reaching this point, you might be questioning a choice of words I made. I chose to say "nothing bad can happen to the user if he or she follows the above protocol." But you might reply, "I found something bad! It's at step #3. The user pays an invoice at step #3 before the server has actually done anything for them, so if the server decides to abort at any point between step 3 and step 7, the user will lose that money and have nothing to show for it."
You're right. That is something bad, and it is also a trust assumption that applies to "regular" swap servers. But I think it is minor: papa swap fees will, hopefully, be very low, due to a kind of "race to the bottom." Anyone can run a papa swap server (it basically just requires some base layer bitcoin and a lightning wallet) so if anyone is charging high fees, someone else can spin up a server and take their business. So I hope that means no one can really get away with charging anything but basement rates. And thus your fee invoice will be low, and the "something bad" you found is just "a swap server can take a tiny little fee from you without providing you with service." But you're right, that is something bad. So I recommend not using a papa swap server if you doubt its trustworthiness, unless you're willing to lose a few sats for science's sake.
The name Papa Swap comes from Papa Class, a group of Cold War submarines that included the world’s fastest declassified submarine – the Soviet K-222. As those are the fastest submarines, these are the fastest submarine swaps. Well, actually, now that I think about it, they are the second fastest. There is another type of swap used in some lightning wallets, called a "splice in," and it is sometimes safe to use a channel funded via a splice even before the splice transaction confirms. (The occasions when this is safe are probably outlined and explained in other documents about how splicing works, but I couldn't find any within 5 minutes of doing a quick google search; you try it and maybe you'll have better luck.) So splices are, at least sometimes, faster than papa swaps.