10

When executing a Bitcoin Script, there are some "special cases" where the interpreter performs extra verification beyond just executing scriptSig and then scriptPubKey.

For example, if the scriptPubKey has the following specific format:

OP_HASH160 <20 byte hash> OP_EQUAL

then the interpreter will additionally apply the BIP 0016 "Pay to Script Hash" (P2SH) rules.

What's the full list of "special cases" that could be encountered during Script execution, and when is each encountered?

Elliott
  • 227
  • 1
  • 5

1 Answers1

9

As of January 2021, "high-level" script validation with all active consensus rules on the network is approximately this:

Top level evaluation

  • Execute the scriptSig, and call the resulting stack stack. If execution aborts, fail.
  • Execute the scriptPubKey with stack as input, and call the resulting stack result. If execution aborts, fail.
  • If result is empty, or its top element has numerical value 0, fail.
  • If scriptPubKey is exactly equal to an OP_n (with n between 0 and 16, inclusive) followed by a direct push of exactly 2 to 40 bytes inclusive:
    • If scriptSig not empty, fail.
    • Run segwit validation with the 2-to-40 byte push in scriptPubKey as program, the n value as version, and witness as input (see further). If this execution aborts, fail.
  • If scriptPubKey is exactly equal to OP_HASH160 + a 20 byte push + OP_EQUAL, run P2SH validation:
    • If scriptSig does not consists of only pushes, fail.
    • If result is empty, fail.
    • Interpret the top element of result as a script, and execute it, with the rest of result as input. Call the resulting stack p2sh_result. If this execution aborts, fail.
    • If p2sh_result is empty, or its top element has numerical value 0, fail.
    • If the top element of result is exactly OP_n (with n between 0 and 16 inclusive) followed by a direct push of 2 through 40 bytes inclusive:
      • If scriptSig is not exactly a direct push of the top element of result, fail.
      • Run segwit validation with the 2-to-40 byte push in the top element of result as program, the n value as version, and witness as input (see further). If this execution aborts, fail.
  • If no failure occurred before this point, the input is valid.

Segwit validation

Segwit validation for version version, with program program, and input input:

  • If the version is 0:
    • If the program is not 20 or 32 bytes, fail.
    • If the program is 20 bytes hash:
      • Execute the script OP_DUP OP_HASH160 hash OP_EQUALVERIFY OP_CHECKSIG, with initial stack input. If execution aborts, fail.
      • If the resulting stack is not exactly one element, or that element has numerical value 0, fail.
    • If the program is 32 bytes hash:
      • If input is empty, or its top element's SHA256 hash does not equal hash, fail.
      • Execute the top element of input as script, with the other elements as input. If execution aborts, fail.
      • If the resulting stack is not exactly one element, or that element has numerical value 0, fail.
  • If no failure occurred up to this point, return success.

This answer does not include the changes introduced by BIP341 and BIP342, as these are not active on the network. It also does not include various standardness/policy rules.

Source: https://github.com/bitcoin/bitcoin/blob/v0.20.1/src/script/interpreter.cpp, functions VerifyScript, VerifyWitnessProgram, ExecuteWitnessScript.

TL;DR: only the BIP16 P2SH pattern (OP_HASH160 <20 bytes> OP_EQUAL) and the BIP141 segwit pattern (OP_n <2 to 40 bytes>) are actual special cases that trigger additional rules. But the details are more complex.

Pieter Wuille
  • 98,249
  • 9
  • 183
  • 287
  • 1
    In the P2SH wrapped case, should this be "Run segwit validation with the top element of `result` as program" (instead of `scriptPubKey` as program)? – Elliott Mar 26 '21 at 17:38
  • @Elliott Fixed. – Pieter Wuille Aug 31 '23 at 14:58
  • @PieterWuille For P2SH validation you wrote: "If scriptSig does not consists of only pushes, fail." But the redeem script (i.e. the top element of `result`) can contain whatever OP codes we want? – joke Sep 01 '23 at 15:58
  • Yes, the scriptSig must consist of only pushes. The last of those push is the redeemscript, which *when the data being pushed is interpreted as a script* contains opcodes. – Pieter Wuille Sep 01 '23 at 16:19
  • @PieterWuille I didn't ask the question well. I wanted to say that the redeem script can contain any opcodes we want, they don't have to be push only? – joke Sep 01 '23 at 16:26
  • 1
    Yes, the redeemscript is any script, it can contain any opcodes. It's just the scriptSig that must be push only for P2SH spends. – Pieter Wuille Sep 01 '23 at 16:26
  • @PieterWuille Taproot (BIP341, BIP342) doesn't break any of the rules you wrote in your answer? It would represent just another "if the version is 1" in the Segwit validation section with some rules of its own? – joke Sep 01 '23 at 16:37
  • 1
    @joke Correct, it's just adding a "if version is 1, witness program is 32 bytes, ..." branch. – Pieter Wuille Sep 01 '23 at 16:38
  • @PieterWuille Couple of additional questions. 1) For P2WSH in P2SH you wrote: "If `scriptSig` is not exactly a direct push of the top element of `result`, fail.". Does it mean that in this case any additional pushes are not allowed (opposite to P2SH)? So only the push of the redeem script and the redeem script is "OP_n (with n between 0 and 16, inclusive) followed by a direct push of exactly 2 to 40 bytes". 2) For P2WSH you wrote "If `input` is empty, or its top element's SHA256 `hash` does not equal hash, fail.", is it more accurate to say `the last item of witness data` since it's not stack? – joke Sep 01 '23 at 23:35
  • @PieterWuille 3) For P2WSH you wrote "Execute the top element of `input` as script, with the other elements as input. If execution aborts, fail.". Are these other elements i.e. the other elements in the witness data, the initial stack? 4) Do these other items have to be push only, like in classic P2SH, or can some other OP codes as well? – joke Sep 01 '23 at 23:35
  • 1) Yes, no other pushes are allowed (because inputs need to provided through witness, not through scriptSig). 2) The list of witness items is called a stack too, though admittedly that name isn't very accurate. But yes, last element. 3) Yes, for P2WSH the witness stack consists of the witness script's inputs, followed by the witness script itself. All but the last element become the initial stack for execution. 4) The witness stack is not a script, it's just a list of elements; it has no notion of opcodes or pushes - rather, it's the resulting script stack itself. – Pieter Wuille Sep 02 '23 at 00:41
  • @PieterWuille Okay, for the last (4) question/answer. In P2SH, the scriptSig consists only of OP_CODES for push + the data being pushed and the last push is the redeem script. In P2WSH, there is no push OP_CODES in the witness script, it is just one element next to another, which will be placed in order on the stack, while the last one will represent the redeem script itself which will work with previous items as initial stack. So there are no classic: op_code_push data, op_code_push data etc. as in P2SH, instead it goes: data data data (last data = redeem script) etc.? – joke Sep 02 '23 at 00:52
  • 1
    All correct. The witness stack is just an array of byte arrays. For P2WSH the last one is interpreted as the witness script, the rest becomes the initial script execution stack. – Pieter Wuille Sep 02 '23 at 01:03