Hello. I would like to discuss the feasibility of implementing a SIGHASH_DOUBLE flag, which would sign for two outputs in a transaction.

I would like this feature as an option when signing transactions, so that I can create a PSBT that can be combined with similar PSBTs in a non-interactive way (similar to SIGHASH_SINGLE), that includes both a payout output and a change output.

In regards to how one would verify a tx with such a flag present, this would be my naive approach:

Imagine we have a tx with four inputs, six outputs, and the following config:

  1. Scan all signature flags in a tx, check if SIGHASH_DOUBLE exists.

  2. If true, we will count two indices in the output index:

  • one index starting at 0: (n0).
  • one index starting at vin_count + 1: (n4).
  1. Initially, treat SIGHASH_DOUBLE inputs similar to SIGHASH_SINGLE, by pairing them with their adjacent output. This gives us consensus on the index of the first output: (vin0/vout0) and (vin1/vout1).

  2. For each SIGHASH_DOUBLE input, we will also observe the index starting at vin_count + 1 (n4). This gives us consensus on the second output: (vin0/vout4) and (vin1/vout5).

That’s about it. Essentially we use vin_count to offset a second index for the outputs > vin_count. This should work for SIGHASH_DOUBLE, SIGHASH_TRIPLE, or as high as you want to go.

I believe this would be simple enough for consensus on validating signatures. It may help to organize inputs/outputs based on their sighash flag, so that when you are consolidating PSBTs:

  • SIGHASH_DOUBLE pairs goes first,
  • then SIGHASH_SINGLE pairs,
  • then remaining inputs,
  • then SIGHASH_DOUBLE outputs,
  • then remaining outputs.

Worst-case, your tx gets rejected from the mempool, because you didn’t organize things properly.

Edit: I forgot to mention the odd case where you contribute inputs without an output. I’m not sure how often that comes up, but I don’t believe these inputs would be compatible with a SIGHASH_DOUBLE flag. Which I think is okay, as the worst-case is still just your tx being rejected from the mempool.

Edit2: This also wouldn’t work with other PSBTs that consolidate inputs, as they will be contributing more inputs than outputs. Not sure how to get around that, other than making SIGHASH_DOUBLE only compatible with SIGHASH_SINGLE. But maybe there is a way.

As for how this could be soft-forked into bitcoin, I would look at some way of extending the SIGHASH_NONE flag, so that older nodes will skip validation. I believe taproot introduced a few ways of extending signature validation, though I’m not sure which method would be best to use.

Another interesting use case for SIGHASH_DOUBLE (with ANYONECANPAY), is that you can have a mempool of single-spend PSBTs. This wouldn’t be for coin laundering, but to save 7-8 vbytes of overhead per tx, as miners could just consolidate these into a final tx when making a block.

Anyway, it would be a nice feature to have, and it feels like a missing flag option to sign for both a payout and change output in a composable way.

I haven’t put a terrible amount of thought into this, so any feedback from smart minds would be greatly appreciated.


1 Like

Thank you for posting the link, I have not heard of this proposal!

Yeah the more I think about it, it seems that for any soft-fork change to SIGHASH is going to require stuffing data somewhere else in a tx, so might as well add some index information for composing PSBTs.

Maybe this is the better way to do it? What would it take to make this a more formal proposal that could be included in a soft fork change?

i think the main issue is the lack of flexibility and expressiveness compared to something like TXHASH, the other is you need a new script version to define new flags.

in case the consensus emerges that we can’t do TXHASH, it would be worthwhile to reexamine SIGHASH_BUNDLE and APO/AS. especially if we say do a tarpoot 2.0 where we fix the biggest blunders of taproot, then including new sighash modes in that script version might be worthwhile.