TeachMeBitcoin

Why MUL, DIV, and MOD Are Disabled in Bitcoin

From TeachMeBitcoin, the free encyclopedia Reading time: 6 min

20. Why MUL, DIV, and MOD Are Disabled in Bitcoin

The Missing Opcodes

Among the arithmetic opcodes defined in Bitcoin's original opcode table, three stand out as conspicuously absent from active use:

OP_MUL    — Multiply two numbers          (0x95) — DISABLED
OP_DIV    — Divide two numbers            (0x96) — DISABLED
OP_MOD    — Modulo of two numbers         (0x97) — DISABLED
OP_LSHIFT — Left bit shift                (0x98) — DISABLED
OP_RSHIFT — Right bit shift               (0x99) — DISABLED

These opcodes are defined in the opcode table and their hex values are reserved, but any script that uses them will fail immediately. In Bitcoin Core's interpreter.cpp:

case OP_MUL:
case OP_DIV:
case OP_MOD:
case OP_LSHIFT:
case OP_RSHIFT:
    return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE);

History: Present at Genesis, Then Removed

These opcodes were present in Bitcoin's original implementation (v0.1, released January 2009). Satoshi Nakamoto included them as part of a general-purpose scripting system. However, they were disabled relatively quickly — by August 2010 — in version 0.3.10, in the same patch that also disabled several other potentially dangerous opcodes.

Bitcoin v0.1.0 (January 2009):  OP_MUL, OP_DIV, OP_MOD are active
Bitcoin v0.3.10 (August 2010):  OP_MUL, OP_DIV, OP_MOD are disabled
Reason stated in commit:        "misc fixes" — no detailed public explanation

The disabling happened in the aftermath of several critical vulnerability discoveries in Bitcoin Script. The broader context was Satoshi tightening the scripting system after recognizing that some opcodes created unpredictable attack surfaces.

Reason 1: Integer Overflow and Undefined Behavior

The original implementation of OP_MUL used standard C++ integer arithmetic, which is subject to undefined behavior on overflow for signed integers. If a script pushed two large numbers and multiplied them, the result could:

Since Bitcoin requires consensus — every node must compute the exact same result for every script — any source of non-determinism is catastrophic.

// Hypothetical original OP_MUL (broken):
// What happens if bn1 = 2,000,000 and bn2 = 2,000,000?
// Result = 4,000,000,000,000 — far exceeds int32 range
// Undefined behavior in C++, inconsistent across nodes = consensus failure
int32_t result = bn1 * bn2;  // OVERFLOW!

With CScriptNum capped at 4 bytes (32-bit range), any multiplication of two 32-bit numbers can produce a 64-bit result that overflows. Handling this safely requires defining overflow behavior precisely — which was not done.

Reason 2: Division by Zero

OP_DIV and OP_MOD introduce the possibility of division by zero. In C++, integer division by zero is undefined behavior and typically causes a crash:

// Hypothetical OP_DIV (broken):
CScriptNum result = bn1 / bn2;
// If bn2 == 0: CRASH / undefined behavior / node goes down

An attacker could craft a transaction containing a division-by-zero script and broadcast it. Any Bitcoin node that tried to validate this transaction would crash. This is a Denial of Service (DoS) vulnerability that could take down a significant portion of the network.

While this could be fixed with a simple check (if (bn2 == 0) return error), the decision was made to disable the opcodes entirely rather than patch them piecemeal.

// Safe but not implemented:
if (bn2 == 0)
    return set_error(serror, SCRIPT_ERR_DIV_BY_ZERO);
CScriptNum result = bn1 / bn2;

Reason 3: Consensus-Critical Behavior of Signed Division

Even if overflow and zero-division were handled, there is still the question of what signed division and modulo mean for negative numbers. Different languages, compilers, and CPU architectures handle the sign of the remainder differently:

In most C++ implementations (truncation toward zero):
  7 / -2 = -3   (remainder = +1)
 -7 /  2 = -3   (remainder = -1)
 -7 / -2 = +3   (remainder = -1)

In Python (floor division):
  7 // -2 = -4  (remainder = -1)
 -7 //  2 = -4  (remainder = +1)

Bitcoin requires every node to agree. If even one node has a different integer division semantic (perhaps because it runs on a different architecture, or is compiled with a different compiler flag), the network will split into two incompatible chains — a consensus failure.

Defining a canonical, cross-platform, cross-architecture integer division for all possible inputs is non-trivial. The safest fix in 2010 was to disable the opcodes entirely.

Reason 4: Script Complexity and DoS via Computation

Multiplication enables polynomial-time computation that far exceeds what addition alone allows. An attacker could craft a script that performs repeated multiplications to make script validation arbitrarily slow:

// Hypothetical script bomb:
<huge_number> <huge_number> OP_MUL OP_MUL OP_MUL ...  (repeated)

Bitcoin Script has no loop constructs, but within a script's flat opcode sequence, a sequence of OP_MUL operations on large numbers could create significant CPU load per validation. Without strict size and operation limits, this creates a DoS vector.

The 10,000 opcode limit and 520-byte stack element limit exist for similar reasons, but multiplication's ability to produce rapidly growing numbers makes it particularly dangerous.

Reason 5: Philosophical Conservatism

After the overflow vulnerability discovery of August 2010 (the infamous "1 RETURN" exploit and the value overflow incident), Satoshi adopted a philosophy of conservative minimalism for the scripting system. The principle was:

"Don't add capabilities that aren't strictly necessary for what Bitcoin needs to do right now."

Bitcoin's primary use case — verifying digital signatures and hash preimages — does not require multiplication, division, or modulo. The existing arithmetic opcodes (add, subtract, comparisons) suffice for all legitimate use cases that had been identified. Keeping disabled opcodes as disabled reduces the attack surface.

Current Status and Future Prospects

As of today, these opcodes remain disabled in Bitcoin mainnet. However, there have been serious discussions and proposals to re-enable some of them, particularly in the context of:

Bitcoin Cash (BCH) re-enabled OP_MUL, OP_DIV, and OP_MOD in 2018. Their implementation addressed the historical issues:

BCH OP_DIV / OP_MOD behavior:

- Division by zero → script failure (clean error, no crash)

- Result type: CScriptNum with proper overflow checks

- Signed semantics: truncation toward zero (C-style), clearly specified

Tapscript (BIP 342) did not re-enable them, maintaining the conservative approach. Future soft forks or Tapscript upgrades (like OP_CAT, OP_TLUV, or new arithmetic opcodes under discussion) may revisit the question.

The bottom line is that re-enabling these opcodes is technically feasible with proper safeguards, but:

  1. It requires a soft fork (consensus change) and community agreement.

  2. The implementation must handle overflow, zero-division, and signed semantics unambiguously.

  3. The benefits must outweigh the risk of introducing subtle consensus bugs in the most critical piece of financial infrastructure in the world.

Summary Table

``` Opcode | Status | Primary Risk

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