TeachMeBitcoin

OP_VERIF and OP_VERNOTIF - Always-Invalid Opcodes

From TeachMeBitcoin, the free encyclopedia Reading time: 3 min

18. OP_VERIF and OP_VERNOTIF — Always-Invalid Opcodes

Overview

OP_VERIF (byte 0x65) and OP_VERNOTIF (byte 0x66) are two opcodes that share a unique and extreme status in Bitcoin Script: they are always invalid regardless of context. Unlike disabled opcodes (which fail during execution) or OP_RESERVED (which fails if executed), OP_VERIF and OP_VERNOTIF cause immediate script invalidity even if they appear inside an OP_IF/OP_ENDIF block that is never entered.

Historical Origin

These opcodes were originally intended to complement OP_VER. The envisioned semantics were:

OP_VERIF:    Execute the next block if the protocol version matches
OP_VERNOTIF: Execute the next block if the protocol version does NOT match

They would have functioned like OP_IF and OP_NOTIF, but conditioned on the protocol version rather than a stack item. This design was abandoned along with OP_VER for similar reasons: version-conditional script logic creates unresolvable consensus problems as the protocol evolves.

Always-Invalid Behavior

The distinguishing characteristic of OP_VERIF and OP_VERNOTIF is that they fail even in unexecuted branches. This is sometimes described as "super-disabled":

Case 1: In main execution path
  Script: OP_1 OP_VERIF OP_2

Result: ALWAYS INVALID

Case 2: In false branch of OP_IF
  Script: OP_0 OP_IF OP_VERIF OP_ENDIF

Result: ALWAYS INVALID (even though branch is never entered)

Case 3: In nested false branch
  Script: OP_0 OP_IF OP_0 OP_IF OP_VERIF OP_ENDIF OP_ENDIF

Result: ALWAYS INVALID

This is in contrast to OP_RESERVED:

Script: OP_0 OP_IF OP_RESERVED OP_ENDIF

Result: VALID (OP_RESERVED in dead branch is fine)

Script: OP_0 OP_IF OP_VERIF OP_ENDIF

Result: INVALID (OP_VERIF anywhere is always invalid)

Implementation in Bitcoin Core

// From src/script/interpreter.cpp
// OP_VERIF and OP_VERNOTIF are explicitly invalid
// They appear in the 'always invalid regardless of fExec' check
if (opcode == OP_VERIF || opcode == OP_VERNOTIF)
    return set_error(serror, SCRIPT_ERR_BAD_OPCODE);

This check runs unconditionally, before the fExec (execution flag) is consulted. Even if the interpreter is in a "skip" state (inside a false conditional branch), encountering OP_VERIF or OP_VERNOTIF causes immediate failure.

Why This Property Matters

The "always invalid even in dead branches" property has an important implication: any script that contains OP_VERIF or OP_VERNOTIF can never be valid, and therefore any output locked with such a script is permanently unspendable. This is stronger than OP_RETURN (discussed in the next section) in the sense that the invalidity cannot be worked around by clever script construction.

It also means that these opcodes cannot be used as upgrade vehicles via soft forks (unlike NOP opcodes), since any scripts using them are already irreversibly invalid.


☕ Help support TeachMeBitcoin

TeachMeBitcoin is an ad-free, open-source educational repository curated by a passionate team of Bitcoin researchers and educators for public benefit. If you found our articles helpful, please consider supporting our hosting and ongoing content updates with a clean donation:

Ethereum: 0x578417C51783663D8A6A811B3544E1f779D39A85
Bitcoin: bc1q77k9e95rn669kpzyjr8ke9w95zhk7pa5s63qzz
Solana: 4ycT2ayqeMucixj3wS8Ay8Tq9NRDYRPKYbj3UGESyQ4J
Address copied to clipboard!