OP_RETURN vs OP_VERIFY - Key Differences
11. OP_RETURN vs OP_VERIFY — Key Differences
Overview
Both OP_RETURN and OP_VERIFY can terminate script execution prematurely, but they serve entirely different purposes and have fundamentally different semantics. Confusing them is a common source of misunderstanding for Bitcoin developers.
Quick Reference
OP_VERIFY: Hex 0x69 — Fail if top of stack is false. Unconditional termination on failure.
OP_RETURN: Hex 0x6a — Mark output as unspendable (provably prunable). Always "fails."
OP_RETURN — The Unspendable Marker
OP_RETURN was repurposed in Bitcoin Core 0.9.0 (March 2014) to allow embedding arbitrary data in transactions without creating unspendable UTXOs in the UTXO set. When a scriptPubKey begins with OP_RETURN, the output is:
-
Provably unspendable — no valid scriptSig can ever satisfy it.
-
Prunable — nodes are not required to track it in the UTXO set.
-
Data-bearing — up to 83 bytes (80 bytes of data + 3 bytes of overhead) can follow.
; OP_RETURN data embedding:
OP_RETURN <arbitrary_data>
; Example — embedding a document hash:
OP_RETURN 0x6f6d6e69 <sha256_document_hash>
; Common use cases:
; - Colored coins / token protocols (Omni, RGB)
; - Timestamping services (OpenTimestamps)
; - NFT metadata pointers
; - Cross-chain anchoring
OP_VERIFY — The Assertion Opcode
OP_VERIFY, as covered in sections 9 and 10, is used within spending scripts to assert conditions. It appears in scriptPubKey and scriptSig/witness contexts.
; OP_VERIFY in a spending context:
<computed_value>
<expected_value>
OP_EQUAL
OP_VERIFY ; fail if computed != expected
Side-by-Side Comparison
| Property | OP_VERIFY | OP_RETURN |
|---|---|---|
| Purpose | Assert condition mid-script | Mark output as unspendable / embed data |
| Location | Inside scriptPubKey or scriptSig | At start of scriptPubKey only |
| Stack interaction | Pops top element | None (terminates immediately) |
| Script continues? | Yes (if condition true) | Never |
| Use in witness | Yes | No |
| UTXO set | N/A (appears in spending script) | Output excluded from UTXO set |
| Data after opcode | Not applicable | Up to 80 bytes of arbitrary data |
OP_RETURN in scriptPubKey
; OP_RETURN scriptPubKey format:
6a ; OP_RETURN (0x6a)
<pushdata> ; optional data following
; Example with 20 bytes of data:
6a 14 <20_bytes_of_data>
; The UTXO is created but immediately prunable
; No scriptSig can ever satisfy OP_RETURN
Can OP_RETURN Appear Mid-Script?
Prior to the OP_RETURN standardization, some implementations treated OP_RETURN mid-script as simply failing at that point. However, in modern Bitcoin:
-
OP_RETURNin scriptPubKey (as the first opcode) marks the output unspendable. -
OP_RETURNappearing inside a spending script (scriptSig/witness) is handled as a script termination failure — but this usage is non-standard and should be avoided. -
Use
OP_VERIFYfor conditional failure within scripts.
When Developers Confuse Them
; WRONG — developer tries to embed data in a spendable output using OP_RETURN:
<pubkey> OP_CHECKSIG OP_RETURN <data>
; This is non-standard and makes the output behave unexpectedly
; CORRECT — embed data in a separate OP_RETURN output:
; Output 0: P2PKH (spendable)
; Output 1: OP_RETURN <data> (data carrier, zero value)
; WRONG — developer tries to use OP_RETURN as an assertion:
<value> OP_1 OP_EQUAL OP_RETURN ; does not work as intended
; CORRECT — use OP_VERIFY:
<value> OP_1 OP_EQUAL OP_VERIFY
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: