Glossary

OP_IF / OP_NOTIF

Bitcoin Script flow control opcodes that enable conditional execution paths, essential for complex contracts like HTLCs.

Key Takeaways

  • OP_IF (0x63) and OP_NOTIF (0x64) are Bitcoin Script opcodes that enable conditional branching: OP_IF executes its body when the top stack value is true, while OP_NOTIF executes when the value is false.
  • These opcodes are essential for Hash Time-Locked Contracts (HTLCs), where OP_IF separates the hash-reveal path from the timelock refund path in a single script.
  • Taproot's MAST structure reduces the need for OP_IF by placing each spending condition in a separate Merkle tree leaf, improving both privacy and efficiency.

What Is OP_IF?

OP_IF is Bitcoin Script's flow control opcode. It pops the top element from the stack and, if the value is non-zero (true), executes the opcodes that follow until it reaches an OP_ELSE or OP_ENDIF. If the value is zero (false), execution skips ahead to the OP_ELSE branch or OP_ENDIF. OP_NOTIF works identically but with inverted logic: it executes its body when the top stack value is false.

Together with OP_ELSE (0x67) and OP_ENDIF (0x68), these opcodes give Bitcoin Script the ability to express multiple spending conditions within a single output. Without OP_IF, every output could only have one way to be spent. With it, a script can offer different paths depending on what data the spender provides: a signature, a hash preimage, or proof that a timelock has expired.

Unlike conditionals in most programming languages, OP_IF does not evaluate an inline expression. Bitcoin Script is stack-based: the condition must already be on the stack before OP_IF is encountered. The spender controls which branch executes by pushing TRUE or FALSE onto the stack as part of their unlocking script (witness data).

How It Works

The fundamental pattern for conditional execution in Bitcoin Script follows this structure:

<condition>
OP_IF
  <opcodes for true path>
OP_ELSE
  <opcodes for false path>
OP_ENDIF

The <condition> value comes from the spender's witness data. OP_IF pops this value, evaluates it, and routes execution accordingly. OP_ELSE is optional: a script can use OP_IF with only OP_ENDIF if no alternative path is needed. Every OP_IF must have a matching OP_ENDIF, or the script is invalid.

Boolean Evaluation Rules

Bitcoin Script defines "false" as the empty byte array, zero, or negative zero (0x80). Any other value evaluates to true. Byte vectors are interpreted as little-endian integers with the most significant bit as a sign bit, so multiple byte representations of zero all evaluate to false.

In Tapscript (BIP 342), the MINIMALIF rule tightens this: the input to OP_IF must be exactly the empty vector (false) or 0x01 (true). Any other value causes immediate script failure. This is a consensus rule in Tapscript, eliminating a malleability vector where third parties could substitute different truthy values without invalidating the transaction.

Nesting OP_IF

OP_IF blocks can be nested to create more complex branching logic. There is no explicit consensus rule limiting nesting depth, but practical constraints apply. Legacy and SegWit v0 scripts are limited to 10,000 bytes and 201 non-push opcodes, which bounds nesting to roughly 100 levels. Tapscript removes both limits, constrained only by block weight.

HTLC Script Example

The most important real-world use of OP_IF is in HTLCs, as defined in BIP 199. The script creates two spending paths:

OP_IF
  OP_SHA256 <digest> OP_EQUALVERIFY
  OP_DUP OP_HASH160 <seller_pubkey_hash>
OP_ELSE
  <timeout> OP_CHECKLOCKTIMEVERIFY OP_DROP
  OP_DUP OP_HASH160 <buyer_pubkey_hash>
OP_ENDIF
OP_EQUALVERIFY
OP_CHECKSIG

The OP_IF branch (hash path) lets the recipient claim funds by revealing the preimage that hashes to the digest, plus a valid signature. The spender pushes TRUE onto the stack to select this branch.

The OP_ELSE branch (timeout path) lets the sender reclaim funds after the CLTV timelock expires. The spender pushes FALSE to select this branch. The timelock opcodes ensure this path is only valid after the specified block height or time.

Use Cases

Hash Time-Locked Contracts

HTLCs are the foundation of the Lightning Network's payment routing. Every multi-hop Lightning payment uses an HTLC script with OP_IF to separate the hash-reveal path from the timeout-refund path. The receiver claims funds by revealing the preimage; if they fail to do so before the timelock expires, the sender recovers their funds through the OP_ELSE branch. For a deeper look at this mechanism, see the research article on Bitcoin Script programmability.

Escrow and Dispute Resolution

OP_IF enables multi-party escrow without trusted intermediaries. A script can offer one path requiring signatures from both buyer and seller (cooperative settlement) and an alternative path requiring an arbiter's signature after a delay (dispute resolution). Each path lives inside an OP_IF or OP_ELSE block, and the appropriate branch is selected at spending time.

Vault Constructions

Bitcoin vault scripts use OP_IF to distinguish between normal withdrawals and emergency recovery. One branch allows spending after a time delay (giving the owner time to detect theft), while the other allows immediate spending with a recovery key. For more on this pattern, see Bitcoin Script vaults explained.

Atomic Swaps

Cross-chain atomic swaps use OP_IF scripts on both chains with a shared hash. One party reveals the preimage to claim on one chain (OP_IF branch), which exposes the preimage for the other party to claim on the second chain. The OP_ELSE branch on each side provides a timeout refund if the swap is abandoned.

Taproot and MAST: Beyond OP_IF

Taproot (activated November 2021 via BIP 341) introduced Merkelized Alternative Script Trees (MAST), which fundamentally change how conditional logic is expressed on Bitcoin.

With traditional OP_IF scripts, all execution paths are encoded in a single script that must be fully revealed on-chain when spending. If a script has five possible spending conditions, all five are exposed even though only one is used. This leaks information about the contract's structure and increases transaction size.

MAST places each execution path in a separate leaf of a Merkle tree. When spending, only the executed leaf and its Merkle proof are revealed. The other paths remain completely hidden. BIP 341 explicitly recommends this approach: "When deciding between scripts with conditionals (OP_IF etc.) and splitting them up into multiple scripts, it is generally preferable to pick the latter."

That said, OP_IF is not obsolete. Within a single Taproot leaf, OP_IF can still be more efficient for closely related conditions where the Merkle proof overhead (32 bytes per tree level) exceeds the cost of including both branches. Simple two-path scripts may be better served by OP_IF inside a single leaf than by splitting into separate leaves. For a comprehensive overview, see the research article on Taproot and Schnorr signatures.

Risks and Considerations

Script Complexity and Verification Cost

Deeply nested OP_IF structures increase script verification time. Bitcoin Core's original implementation tracked conditional state using a boolean vector scanned on every opcode, producing O(n²) validation time for deeply nested scripts. This was identified as a potential denial-of-service vector, particularly in Tapscript where the 201-opcode limit is removed. The issue was fixed in Bitcoin Core PR #16902 by replacing the vector with two integer counters, reducing the check to O(1).

Data Embedding via OP_FALSE OP_IF

The pattern OP_FALSE OP_IF ... OP_ENDIF creates a never-executed branch that can hold arbitrary data. This technique is used by Ordinals inscriptions to embed images, text, and other content in the witness, bypassing datacarrier size limits that apply to OP_RETURN outputs. The data benefits from the SegWit witness discount, paying only one-quarter the fee weight of non-witness data.

Malleability Without MINIMALIF

In legacy and P2WSH scripts, the input to OP_IF can be any non-zero value for true. A third party could change the witness value (for example, from 0x01 to 0x02) without invalidating the transaction, altering its identifier. In P2WSH, the MINIMALIF rule mitigates this as a relay policy. In Tapscript, MINIMALIF is a consensus rule: only the empty vector and 0x01 are valid OP_IF inputs, fully closing this malleability vector.

Privacy Leakage

When a pre-Taproot OP_IF script is spent, both the taken and untaken branches are revealed on-chain. This exposes the full contract structure to chain observers, who can infer the type of contract (HTLC, escrow, vault) from the script pattern. Taproot's MAST structure and Miniscript tooling help mitigate this by hiding unexecuted paths and standardizing script templates.

This glossary entry is for informational purposes only and does not constitute financial or investment advice. Always do your own research before using any protocol or technology.