OP_IF - Conditional Execution Basics
1. OP_IF — Conditional Execution Basics
Overview
OP_IF is one of the most powerful and foundational opcodes in Bitcoin Script. It introduces the concept of conditional execution — the ability for a script to take different paths based on runtime values on the stack. Without OP_IF, Bitcoin Script would be a purely linear sequence of operations with no branching logic whatsoever. With it, script authors can encode complex spending conditions that adapt to different scenarios, signatories, and time constraints.
At its core, OP_IF peeks at the top item on the main stack. If the value is non-zero (truthy), execution continues into the OP_IF branch. If the value is zero or an empty byte array (falsy), execution skips to the matching OP_ELSE or OP_ENDIF.
Opcode Reference
Opcode: OP_IF
Hex: 0x63
Byte value: 99 (decimal)
Category: Flow Control
How It Works
When the Bitcoin Script interpreter encounters OP_IF, it pops the top element from the stack and evaluates it as a boolean:
-
True (non-zero): The interpreter enters the IF branch and executes opcodes until it hits
OP_ELSEorOP_ENDIF. -
False (zero or empty byte array
0x): The interpreter skips everything until the matchingOP_ELSEorOP_ENDIF.
Stack before OP_IF:
[ ..., <condition> ] <-- top of stack
OP_IF pops <condition>:
if <condition> != 0:
execute IF-branch
else:
skip to OP_ELSE / OP_ENDIF
Basic Syntax
A minimal OP_IF block looks like this:
<condition>
OP_IF
<opcodes executed if condition is true>
OP_ENDIF
With an else branch:
<condition>
OP_IF
<opcodes executed if condition is true>
OP_ELSE
<opcodes executed if condition is false>
OP_ENDIF
Stack Behavior Detail
It is critical to understand that OP_IF consumes the top stack element. The condition value is removed from the stack before the branch body executes. This means whatever value was used as the condition is no longer present inside the branch body.
Initial stack (top on right): [ 0x01 0x02 <1> ]
OP_IF --> pops <1>, enters IF branch
OP_ADD --> operates on 0x01 and 0x02
OP_ENDIF
Result stack: [ 0x03 ]
Compare with a false condition:
Initial stack: [ 0x01 0x02 <0> ]
OP_IF --> pops <0>, skips IF branch
OP_ENDIF
Result stack: [ 0x01 0x02 ] <-- untouched
Script Validation Rules for OP_IF
The Bitcoin consensus rules enforce strict structural requirements for OP_IF:
-
Every
OP_IFmust be closed by a matchingOP_ENDIF. A script without proper closure is immediately invalid. -
Nesting is allowed — an
OP_IFinside anotherOP_IFis perfectly valid (see section 5). -
OP_IFcannot appear in a script without a correspondingOP_ENDIF, even if that branch is never executed at runtime. -
In SegWit v0 (P2WSH) and Tapscript (SegWit v1), there is an additional restriction: the argument to
OP_IFmust be exactly0x00or0x01(the minimal push rule). Passing a non-minimal boolean (such as0x02to mean "true") causes script failure.
; Valid SegWit OP_IF usage
OP_1 ; pushes 0x01 — minimal true
OP_IF
OP_DROP
OP_ENDIF
; Invalid in SegWit — non-minimal boolean
OP_2 ; pushes 0x02 — NOT a minimal boolean
OP_IF ; SCRIPT FAILS in SegWit context
OP_DROP
OP_ENDIF
Why OP_IF Matters
Before OP_IF, every Bitcoin output could only have one fixed spending condition. With OP_IF, you can express logic such as:
-
"Either Alice signs, OR Bob signs AND a timelock has passed."
-
"If a hash preimage is provided, pay Alice; otherwise, after timeout, refund Bob."
-
"Multisig with a primary path and a recovery path."
These use cases underpin technologies like Hash Time Locked Contracts (HTLCs), Lightning Network channels, DLCs (Discreet Log Contracts), and Taproot script trees.
Simple Example: Choosing Between Two Keys
; Locking script (scriptPubKey)
OP_IF
<Alice_pubkey> OP_CHECKSIG
OP_ELSE
<Bob_pubkey> OP_CHECKSIG
OP_ENDIF
; Unlocking script (scriptSig) for Alice's path:
<Alice_signature> OP_1
; Unlocking script for Bob's path:
<Bob_signature> OP_0
Here, the spending party pushes either OP_1 (to take the Alice path) or OP_0 (to take the Bob path), followed by their respective signature.
Common Pitfalls
-
Forgetting that
OP_IFpops the stack: First-time script authors often assume the condition remains on the stack inside the branch. -
Using non-minimal booleans in SegWit: Legacy scripts tolerated this, but modern scripts strictly require
0x00or0x01. -
Leaving the stack imbalanced: Both the true and false branches should generally leave the same number of items on the stack, or final stack validation will fail.
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: