Disabled Opcodes in Tapscript
4. Disabled Opcodes in Tapscript
Some opcodes that work in legacy Bitcoin Script are explicitly disabled in Tapscript. Encountering a disabled opcode in a Tapscript causes immediate script failure — not just relay rejection, but consensus-level invalidity.
OP_CHECKMULTISIG and OP_CHECKMULTISIGVERIFY
These are the most important disabled opcodes in Tapscript. Both OP_CHECKMULTISIG (0xae) and OP_CHECKMULTISIGVERIFY (0xaf) cause immediate failure in Tapscript.
The reason is the off-by-one bug. OP_CHECKMULTISIG in legacy Script pops one more item from the stack than it should — a historical bug that became consensus-critical. The workaround is always pushing a dummy OP_0 item at the start of the unlocking script:
// Legacy 2-of-2 multisig unlocking script:
OP_0 <sig1> <sig2>
// The OP_0 is a dummy item consumed by the bug
// In Tapscript, this whole pattern is replaced by OP_CHECKSIGADD:
<sig1> <sig2>
<pk1> OP_CHECKSIGADD
<pk2> OP_CHECKSIGADD
OP_2 OP_EQUAL
// No dummy item needed, no bug
By disabling OP_CHECKMULTISIG entirely in Tapscript rather than just deprecating it, Bitcoin ensures that the bug cannot be introduced into new Tapscript-based contracts.
Opcodes Disabled in Legacy Script That Remain Disabled
Several opcodes were disabled in legacy Script in August 2010 due to security vulnerabilities. These remain disabled in Tapscript as well. However, many of them are assigned OP_SUCCESS status, meaning a future soft fork could re-enable them with safe semantics:
Disabled in both legacy and Tapscript (currently OP_SUCCESS in Tapscript):
- OP_CAT (0x7e) — string concatenation
- OP_SUBSTR (0x7f) — substring
- OP_LEFT (0x80) — left substring
- OP_RIGHT (0x81) — right substring
- OP_INVERT (0x83) — bitwise invert
- OP_AND (0x84) — bitwise AND
- OP_OR (0x85) — bitwise OR
- OP_XOR (0x86) — bitwise XOR
- OP_2MUL (0x8a) — multiply by 2
- OP_2DIV (0x8b) — divide by 2
- OP_MUL (0x95) — multiply
- OP_DIV (0x96) — divide
- OP_MOD (0x97) — modulo
- OP_LSHIFT (0x98) — left shift
- OP_RSHIFT (0x99) — right shift
What "Disabled" Means in Context
It is important to distinguish between three categories:
Category 1: DISABLED (fail immediately)
- OP_CHECKMULTISIG in Tapscript
- These cause SCRIPT_ERR_DISABLED_OPCODE
Category 2: OP_SUCCESS (succeed immediately)
- Most formerly-disabled opcodes like OP_CAT
- These will be redefined by future soft forks
Category 3: VALID but redefined
- OP_CHECKSIG: same byte, but now verifies Schnorr instead of ECDSA
- OP_CHECKSIGVERIFY: same, Schnorr
- OP_CHECKSIGADD: new opcode, replaces CHECKMULTISIG functionality
Checking if an Opcode is Disabled
In Bitcoin Core's Tapscript implementation:
// From src/script/interpreter.cpp (Tapscript execution):
bool ExecuteWitnessScript(/* ... */) {
// Check for disabled opcodes before execution
if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY) {
return set_error(serror, SCRIPT_ERR_TAPSCRIPT_VALIDATION_WEIGHT);
}
// Check for OP_SUCCESS
if (IsOpSuccess(opcode)) {
// Whole script succeeds
return set_success(serror);
}
// Normal execution continues
}
Technical Insight
This topic covers essential mechanics for Chapter 11. Understanding these details is key to mastering advanced Bitcoin script constructions like Taproot and specialized covenants.
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: