Subject: [bitcoin-dev] CoinPool should focus on different sighashes

From: [email-masked]
Date: Tue, 03 May 2022 11:15:36 +0200

It seems that the current consensus with Taproot is enough to implement CoinPool. There are no needed changes if we want to form a basic version of that protocol, so it probably should be done now (or at least started, even in some signet or testnet3). Later, if some features like SIGHASH_ANYPREVOUT (or the same with ANYSCRIPT) would be added, then it could be improved, just in the same way, as Lightning Network is moving from P2WSH to P2TR.

To start with, we assume there are six participants that are going to form a CoinPool. They start their journey by moving all of their coins to a 6-of-6 multisig channel. Nothing is signed, first we focus on making transactions, then we will sign them backwards to make it trustless.

+------------------------------------------------+
| Alice   3.51 -> ABCDEF (6-of-6 multisig) 19.94 |
| Bob     0.90                                   |
| Charlie 6.81                                   |
| Daniel  7.29                                   |
| Elaine  0.84                                   |
| Frank   0.60                                   |
+------------------------------------------------+

That first transaction is simple, we could sign it later with SIGHASH_ALL. But it seems we can handle it better: what about SIGHASH_ANYONECANPAY? Then, all outputs (so just one) will be signed, but anyone could attach another inputs if needed. Also, the first person could use SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, but it is optional, and I assume we don't need any "group leader" or "coordinator", so anyone will just use SIGHASH_ANYONECANPAY, they will only switch destination and amount, to form a channel in a P2P way. Channel forming could work in a similar way as mempools in Bitcoin: all messages will fly, until there will be some agreement, and some group will collect all needed signatures. But for now, nothing is signed, so let's go to the next transaction.

To close that channel, a group should still exist, and just detach some participant. Closing the whole group is not an option, we want to encourage CoinPool formation, so we don't want to continuously open and close the whole channel. Let's assume that Alice wants to leave. That means, we should get a group of five participants and Alice, nothing more is needed:

+-----------------------------------------------------------------+
| ABCDEF (6-of-6 multisig) 19.94 -> BCDEF (5-of-5 multisig) 16.43 |
|                                   Alice                    3.50 |
+-----------------------------------------------------------------+

When we talk about sighashes, that case is quite interesting. The group of five participants could use SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, and just bind that to the first output. But Alice cannot use the same sighash, because then her funds could be stolen by miners. She can use SIGHASH_ANYONECANPAY, then all outputs will be protected by her signature. However, combining two different sighashes is not going to work well with Schnorr signatures. For that reason, we need two of them. So, the TapScript branch for that ABCDEF multisig could be: "<pubkeyBCDEF> OP_CHECKSIGVERIFY <pubkeyAlice> OP_CHECKSIG".

Also, this case is even more interesting: Alice could also use SIGHASH_SINGLE | SIGHASH_ANYONECANPAY in that ABCDEF multisig and spend by key, but then she needs some on-chain output. It could be anything, even some output she received from dust attack. Then, she could use SIGHASH_ANYONECANPAY on that dust output, just to protect her coins. The total transaction size will be roughly the same, but then she could hide the whole group under a single Schnorr signature.

The missing part here is of course SIGHASH_ANYPREVOUT, then less transactions are needed. But even without that, the basic protocol can be deployed here and now, the only overhead will be transaction storage, because we need to sign every possible transaction for closing the channel. Also, it will be larger in case where all participants are going to close the channel, then each detachment will be processed in a separate transaction.

When it comes to handling costs, the cost of leaving the channel is paid by the detached person. That means, even if going through the least compressed path is possible, it would be costly for all participants trying to do that, so it should discourage them and encourage to play by the rules, and move all coins through N-of-N multisig.

Also, when we talk about covenants, it is in fact some kind of covenant. By having different sighashes for different signatures, we can easily restrict outputs to any scripts we want (and every participant can just collect other signatures, and later decide, where to send its own coins, so it is possible to pay someone directly, instead of first moving coins to some personal address, so there is one less transaction; tldr: another payment can be done during closing the channel).

So, my conclusion is that changing sighashes is far easier and more powerful than my previous idea of "paying to signatures", so I will probably focus on that instead.