2

I have a few questions regarding the transaction hash preimage when signing with different values of the SIGHASH flag in the case of the P2PKH script.

I know what the transaction hash preimage looks like in case of SIGHASH_ALL, but what does it look like for other values of this flag.

So let's say our transaction has 3 inputs and 4 outputs and we can denote it as T(I1, I2, I3; O1, O2, O3, O4), the preimage in the case of SIGHASH_ALL will be as follows.

Let's start with the input. We take all inputs as they are (its full content). After that, we clear the scriptSig field of all inputs and set the length of the scriptSig field in all inputs to zero (0x00). Then, just for the input we're signing, we set its scriptSig field to the scriptPubKey from the output (UTXO) that this input points to. Also, we will set the length of the scriptSig field in this input to the length of this newly added script scriptPubKey. The length of the scriptSig field in other inputs remains zero (0x00), and the scriptSig field remains empty. When it comes to the outputs, we copy their complete contents and add them to the previously described slightly modified contents of the inputs. Finally, we additionally append 0x01000000, which indicates that it is a SIGHASH_ALL type of signature.

This is how it works in the case of SIGHASH_ALL, but how does it work for other values of this flag.

My assumption is that it is exactly the same process, the only question is which inputs and outputs are taken into account. Therefore, the transaction hash preimages for the different values of the SIGHASH flag are as follows (I took into account the values defined in the Master Bitcoin book):

  1. NONE (0x02) - Signature applies to all inputs, none of the outputs

    • for inputs, absolutely the same story that I described for SIGHASH_ALL applies
    • outputs are not taken into account (they are not part of the transaction hash preimage, similar as scriptSig field of other inputs is also not a part)
    • append 0x02000000 to the end
  2. SIGNLE (0x03) - Signature applies to all inputs but only the one output with the same index number as the signed input

    • for inputs, absolutely the same story that I described for SIGHASH_ALL applies
    • only the content of the output whose index corresponds to the input index is taken into account (eg if we want to sign I2, we will take inputs I1, I2 and I3, but only output O2)
    • append 0x02000000 to the end
  3. ALL|ANYONECANPAY (0x81) - Signature applies to one input and all outputs

    • only the input that we sign is included in the transaction hash preimage, the other inputs, i.e. their content, are not part of the transaction hash preimage in any form (we just ignore the other inputs, a similar scenario that we would have if the transaction had only one input and the SIGHASH_ALL)
    • for outputs, absolutely the same story that I described for SIGHASH_ALL applies (we take the entire content of all outputs)
    • append 0x81000000 to the end
  4. NONE|ANYONECANPAY (0x82) - Signature applies to one input, none of the outputs

    • for inputs, absolutely the same story that I described for 0x81 (ALL|ANYONECANPAY) applies
    • outputs are not taken into account (they are not part of the transaction hash preimage)
    • append 0x82000000 to the end
  5. SINGLE|ANYONECANPAY (0x83) - Signature applies to one input and the output with the same index number

    • for inputs, absolutely the same story that I described for 0x81 (ALL|ANYONECANPAY) applies
    • for outputs, absolutely the same story that I described for 0x03 (SIGNLE) applies
    • append 0x83000000 to the end

So my questions are:

1. Is this how it works?

2. Are there any other values for the SIGHASH flag that I should cover?

3. Is there some related work/paper/standard that explains this (perhaps for other scripts, like P2WPKH etc.)?

4
  • 1
    Not a full answer, but for P2WPKH/P2WSH this is described in BIP143 (the entire document is about just the sighash construction). For P2TR key path spends this is described in BIP341 (see "Common signature message", plus "Taproot key path spending signature validation"). For P2TR tapscript spends this is described in BIP342 (reusing BIP341's "Common signature message", plus its own "Signature validation"). Jul 27, 2023 at 13:53
  • @PieterWuille Thanks for the reply Pieter. I will look at the standards you wrote. Do you perhaps know the answers to the other questions (especially the first). I would say that is the logic to follow.
    – dassd
    Jul 27, 2023 at 13:58
  • I don't have the time to go over that in detail now. There are several answers on this site on the topic of sighash computation, though I'm not sure if any cover sighash flags other than ALL. Jul 27, 2023 at 14:02

1 Answer 1

1

Well, based on this article, the principle I already described is followed with some additions (only those additions are written below):

(if you want to check for yourself, please note that the preimage on the web page is presented in reverse - at the beginning is the mark of the signature type , while at the end is the transaction version)

  1. NONE (0x02)

    • value of sequence field of inputs that are not the input for which we are making signature is set to 0x00000000
    • value of tx_out count field is set to 0x00
  2. SIGNLE (0x03)

    • value of sequence field of inputs that are not the input for which we are making signature is set to 0x00000000
    • the value of the tx_out count field depends on the input/output index (index_s) we are signing; for example, if the index_s = 2, we put the value for count field tx_out_count = index_s + 1, which is 3 in this case
    • outputs to index_s (let's say index_s = 2), so outputs 0 and 1, will have the value field set to 0xffffffffffffffff, and the script length field to 0x00, while the script will not exist at all
    • index_s output we are signing in "standard way" (we just take its entire content)
    • outputs after index_s we don't take into account
  3. ALL|ANYONECANPAY (0x81)

    • we only take into account the input we are signing and we do it in a standard way; other inputs are not taken into account at all (this is different from outputs)
    • all outputs are signed in the manner described for SIGNHASH_ALL (0x01)
  4. NONE|ANYONECANPAY (0x82)

    • for inputs, absolutely the same story that I described above for 0x81 (ALL|ANYONECANPAY) applies
  5. SINGLE|ANYONECANPAY (0x83)

    • for inputs, absolutely the same story that I described above for 0x81 (ALL|ANYONECANPAY) applies
    • for output, absolutely the same story that I described above for 0x03

The strange is that when only one output is signing, then the outputs up to it (up to its index) are taken into account (value at 0xffffffffffffffff and script length at 0x00), while this does not apply to inputs. What confuses me is that on the website, for 0x81, 0x82 and 0x83 it says that the input with index 0 is signed, although actually in the example the input with index 1 is signed. Therefore, I do not know if the inputs up to the one that is signed are also taken into account (as in the case of output), because maybe the writer thought that he is really signing the input with index 0, not 1. So there are no inputs up to it, since it is the first in sequence. Another possibility is that he really wanted to sign the input with index 1 (so the inputs up to it are not really taken into account), but writer did not put a good title of the given section.

Does anyone have any more information, or maybe can confirm this?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.