OP_IF vs OP_NOTIF - When to Use Which
7. OP_IF vs OP_NOTIF — When to Use Which
Overview
Choosing between OP_IF and OP_NOTIF is partly a matter of script design philosophy, partly readability, and partly convention. Both are semantically equivalent (one is the boolean inverse of the other), but the choice affects how spending witnesses are structured and how script authors communicate intent.
Symmetry and Equivalence
; These two scripts are logically equivalent:
; Using OP_IF:
<selector>
OP_IF
<path A>
OP_ELSE
<path B>
OP_ENDIF
; Using OP_NOTIF:
<selector>
OP_NOTIF
<path B> ; branches are swapped!
OP_ELSE
<path A>
OP_ENDIF
The spending witness for the first (OP_IF) version:
-
Path A: push
OP_1as selector -
Path B: push
OP_0as selector
The spending witness for the second (OP_NOTIF) version:
-
Path B: push
OP_1as selector (because NOTIF enters when false... wait, no) -
Actually:
OP_NOTIFenters its branch when the value is false (0)
So: Path B is activated by OP_0, Path A by OP_1 — same witness structure. Only the branch ordering in the script changes.
Convention: OP_IF for the Happy Path
By convention in Bitcoin script design, OP_IF is used when the "normal" or "happy path" is the primary case (activated by OP_1). OP_NOTIF is used when you want to express "if this condition is NOT met, do something special."
; Convention: OP_IF for happy path (payment path in HTLC)
OP_IF
; Payment path: preimage provided
OP_SHA256 <hash> OP_EQUALVERIFY
<recipient_pubkey> OP_CHECKSIG
OP_ELSE
; Timeout path: fallback
<timeout> OP_CHECKLOCKTIMEVERIFY OP_DROP
<sender_pubkey> OP_CHECKSIG
OP_ENDIF
Convention: OP_NOTIF for Absence-Based Logic
OP_NOTIF excels when the condition being tested is naturally a "guard" — you want to take action when something is absent or invalid.
; Check if a value is provided; if not, use default
OP_DUP OP_0 OP_EQUAL ; is the top value zero/empty?
OP_NOTIF
; Value is present — process it
OP_SHA256
OP_ELSE
; Value is absent — use default hash
<default_hash>
OP_ENDIF
Performance Considerations
In Bitcoin, script execution cost is measured in script size (vbytes) and opcode count. OP_IF and OP_NOTIF both consume exactly one byte and one opcode unit. There is no performance difference between them. The choice is purely semantic and organizational.
Readability Guidelines
| Scenario | Recommended Opcode |
|---|---|
| Primary spending condition tested first | OP_IF |
| Timeout / fallback tested first | OP_IF with OP_ELSE for fallback |
| Condition is naturally a negation | OP_NOTIF |
| Mimicking "unless" semantics | OP_NOTIF |
| Standard HTLC scripts | OP_IF (BOLT spec convention) |
Real Code Comparison
; OP_IF approach — Lightning HTLC offered HTLC:
OP_DUP OP_HASH160 <RIPEMD160(remote_pubkey)> OP_EQUALVERIFY OP_CHECKSIGVERIFY
OP_SIZE 32 OP_EQUAL
OP_IF
OP_SHA256 <payment_hash> OP_EQUALVERIFY
OP_2 <local_pubkey> <remote_pubkey> OP_2 OP_CHECKMULTISIG
OP_ELSE
<cltv_expiry> OP_CHECKLOCKTIMEVERIFY OP_DROP OP_CHECKSIG
OP_ENDIF
; OP_NOTIF approach — same logic, rearranged:
OP_DUP OP_HASH160 <RIPEMD160(remote_pubkey)> OP_EQUALVERIFY OP_CHECKSIGVERIFY
OP_SIZE 32 OP_EQUAL
OP_NOTIF
<cltv_expiry> OP_CHECKLOCKTIMEVERIFY OP_DROP OP_CHECKSIG
OP_ELSE
OP_SHA256 <payment_hash> OP_EQUALVERIFY
OP_2 <local_pubkey> <remote_pubkey> OP_2 OP_CHECKMULTISIG
OP_ENDIF
The Lightning Network BOLT specification standardizes the OP_IF approach, so consistency with reference implementations favors OP_IF in HTLC contexts.
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: