TeachMeBitcoin

Disabled Opcodes in Tapscript

From TeachMeBitcoin, the free encyclopedia Reading time: 3 min

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.

☕ 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!