1

When a segwit input is signed, the signature will commit to the witness script (or a p2pkh-like script in case of p2wpkh). This is specified in BIP143:

  • For P2WPKH witness program, the scriptCode is 0x1976a914{20-byte-pubkey-hash}88ac.
  • For P2WSH witness program,
    • if the witnessScript does not contain any OP_CODESEPARATOR, the scriptCode is the witnessScript serialized as scripts inside CTxOut.
    • if the witnessScript contains any OP_CODESEPARATOR, the scriptCode is the witnessScript but removing everything up to and including the last executed OP_CODESEPARATOR before the signature checking opcode being executed, serialized as scripts inside CTxOut. (The exact semantics is demonstrated in the examples below)

What's the rationale behind this? Why doesn't the hash commit to the pubkey script as in legacy (p2pkh and p2sh) payments? This difference complicates signing/verification code a bit, so I suppose there must be some good reason for it.

Maybe my question should be: Why does the hash commit to any script at all for segwit inputs? In legacy signatures, putting scriptPubkey in scriptSig for the current input is a (complicated, see below) way to avoid signature reuse between inputs. But in segwit, this is accomplished with the outpoint part of the hashing algorithm. So couldn't we just skip the scriptCode part?

It seems to me that there is another reason for why we hash the scriptPubkey for legacy signatures and the witness script for segwit signatures, since there are simpler ways to avoid signature reuse:

  • Legacy: We could just have added a dummy byte somewhere in the current input
  • Segwit: Current input's outpoint already solves the problem.
3

I'm just guessing, but by having the transaction commit to the scriptCode, we ensure that the signer knows what script they're signing for. A hardware wallet, for example, can only be certain that outpoint 1234...cdef:0 pays a particular script if we give it the transaction that created that outpoint (so that it can hash that transaction, verify the txid, extract the scriptPubKey, and compare that to a provided witnessScript). Because a transaction (without witness data) can be up to almost a megabyte in size and a spending transaction can refer to thousands of previous transactions, we create a situation where low-resource devices such as hardware wallets have a hard time verifying what they're signing.

By contrast, with BIP143, we need only tell the wallet each of the scriptCodes it's supposed to sign for. BIP141 allows these to be up to 10,000 bytes, which is just 1/100th the maximum size of a transaction. The wallet can examine these scriptCodes, ensure they agree with what the wallet expects, and commit to them in its signature knowing that if the person sending them the scriptCode lied about the actual scriptCode, the signature will be invalid.

This is the same reason the BIP143 signature format commits to the value of each input. Before it had to process previous transactions in order to get their output amounts; now it just accepts whatever data it receives and signs it knowing that, if someone lied to it about the amount, the signature will be invalid.

  • "... knowing that if the person sending them the scriptCode lied about the actual scriptCode, the signature will be invalid." I'm in deep waters here, but I think the signature would be invalid even if it didn't directly commit to the scriptCode, because the signature already indirectly commits to the scriptCode via the outpoint->WSH->scriptCode path. So this doesn't really answer the question, I'm afraid. – Kalle Rosenbaum Feb 21 at 12:58
  • You're correct that commiting to the outpoint commits to the prevout scriptPubKey and that the prevout scriptPubKey commits to the script, but the point of the answer is that proving to wallet that a particular outpoint corresponds to a particular witnessScript requires up to ~1 MB data plus the witnessScript. By commiting directly to the scriptCode, now the only data required for the proof is the scriptCode (which is the same as the witnessScript in normal usage). – David A. Harding Feb 21 at 21:06
  • Consider a transaction with a single p2wsh input. The hardware wallet gets an unsigned transaction (Tx), the supposed input UTXO (U), and the supposed witness script (WS). It also probably gets a key derivation path, but that's irrelevant for this discussion. This information, [Tx, U, WS], doesn't itself prove that the outpoint commits to WS. Further, if the input is signed based on [Tx, U, WS], and if U and WS are false, the signature will be invalid regardless whether the signature commits to scriptCode or not. Am I wrong here? Thank you for your patience. – Kalle Rosenbaum Feb 22 at 20:50
  • Yes, I'm wrong here. Private conversation with @David made me realize that the signature would not be invalid if BIP143 omitted scriptCode. Slip of mind. So yes, we must commit to scriptCode. – Kalle Rosenbaum Feb 23 at 19:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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