Questions and concerns about ZCF
https://gist.github.com/awemany/619a5722d129dec25abf5de211d971bd
In "Creating a Zero Conf Forfeit transaction", it is mentioned that :
... a transaction that has the following structure:
- Inputs:
[P2PKH inputs 1] ... [P2PKH input I]
- Outputs:
[any-type-output 1] ... [any-type-output O] [Forfeit Output]
Later on, the example goes on to show the case where only a single input was used to create the ZCF enabled transaction, but what happens when there are multiple inputs?
Having just one input's pubkey in the forfeit output doesn't work, because any other input can then be used to double spend the payment.
Incompatible with address reuse, on and off chain. Basically any signature over any message is enough to trigger a forfeit.
This includes :
- Past transactions associated with the pubkey (even if they only exist on a fork of the current chain)
- A "Bitcoin Signed Message" signed by the pubkey
These two issues can be solved by the wallet only allowing ZCF for brand new addresses and not allowing to sign text messages with them, but it's flaky at best because the same seed could have been used on a different wallet (which might happen at the time of blockchain forks)
The current scheme doesn't protect from a specially crafted transaction which can be made immune to forfeiture.
Assume the payment is made to two outputs, the actual payment to the seller and the Forfeit output, using two inputs. The seller is to be paid one BCH, and we assume the forfeit output is to be made for at least the same amount. Also assume that 0.1 BCH is a miner incentivising, reasonable fee for a 1 input -> 1 output transaction.
- Input I0 has amount a1 =~ 1.0
- Input I1 has amount a2 =~ 1.2
Essentially, we want the first input amount to just cover, or even be slightly below the amount paid to the seller, and the second input to be larger than the payment (as the scheme suggests), and have enough to cover the fee of the full transaction. Construct the following payment transaction :
- I0[a1] -> O0[1.0] (seller address)
- I1[a2] -> O1[1.1] (forfeiture output)
Here, 2.2 BCH is the total amount in the input, and 2.1 is the total of the outputs, leaving 0.1 as miners' fee, with an additional 1.0 if the miner can spot a double spend attempt.
To make this transaction immune to ZCF :
- Sign I0 normally using the
SIGHASH_ALL
flag - Sign I1 using the
SIGHASH_SINGLE|ANYONECANPAY
flag
It is now possible to construct a double spend using only I1 and O1, as a one input, one output transaction :
- I1[a2] -> O1[1.1] (forfeiture output)
(note that the In subscript doesn't mean "at index n". The indexes of the input and output here are both zero)
This transaction by itself is valid with the same signature for I1 as the one from the payment transaction, and has the same sighash. The total input amount is 1.2, and the total output is 1.1, leaving the same 0.1 fee, for a smaller transaction in terms of bytes.
Additionally, it's possible to add more inputs and outputs to this double spending transaction to make the fee even larger than 0.1, all without invalidating the signature for I1.
This double spend is not punishable by the ZCF because no matter which of the pubkeys in the inputs I0 and I1 was used in the script, neither of them had to provide a second signature for this to work.