Skip to content
buybitcoinsmart

Glossary / Developer reference

OP_VERIFY

Definition
OP_VERIFY pops the top stack item and aborts the entire script unless that item is true, turning any test in Bitcoin Script into a hard requirement.

Byte 0x69 is the language's general purpose assertion. Script can branch with OP_IF, but it has no way to raise an error and no return value beyond whatever survives on the stack, so a contract insists on a condition by computing the answer and handing it to this instruction. Nearly every payment ever made passed through one, usually hidden inside a fused opcode.

How it works

OP_VERIFY removes one item from the stack and asks a single question: is this true?

Truth here is a byte level notion rather than a numeric one. An empty item is false. So is a run of zero bytes, and so is negative zero, a run of zeros ending in 0x80. Everything else counts as true, including a 20 byte hash or a 33 byte public key. If the item is false, the script terminates on the spot and the input holding it is invalid, which invalidates the transaction around it. If the item is true, nothing goes back on the stack and evaluation moves to the next opcode.

That behavior is what lets a script check several things in sequence. A script succeeds only when exactly one true value remains at the end, so without assertions every condition would have to be folded into a single closing boolean. With them, a contract can require a timelock, then require a hash preimage, then require a signature, failing at the first disappointment.

Four opcodes have the assertion built in and exist purely to save a byte: OP_EQUALVERIFY, OP_CHECKSIGVERIFY, OP_CHECKMULTISIGVERIFY and OP_NUMEQUALVERIFY. Because those cover the common cases, a standalone 0x69 is rare on mainnet.

Two opcodes carry VERIFY in the name and do not behave this way at all. OP_CHECKLOCKTIMEVERIFY from BIP65 and OP_CHECKSEQUENCEVERIFY from BIP112 were soft forked onto previously meaningless no-op codes, so to keep older nodes happy they had to leave the stack exactly as they found it. Both inspect the top item and fail when the condition is unmet, but neither consumes it, which is why real scripts always follow them with OP_DROP.

Where you see it

Assertions cluster wherever a script has more than one thing to prove.

A simple time locked vault reads as a public key, OP_CHECKSIGVERIFY, a delay such as 144 blocks (roughly a day), then OP_CHECKSEQUENCEVERIFY. Lightning's commitment and HTLC scripts are built the same way, chaining a preimage check and a signature check so that neither on its own is enough to move money. Taproot script spends lean on the pattern heavily, since tapscript replaced OP_CHECKMULTISIG with OP_CHECKSIGADD and uses verify forms to keep multi-key policies compact.

Miniscript, the policy compiler that several desktop wallets now use to describe spending conditions in readable form, emits these opcodes on your behalf. It is the easiest way to watch assertions being generated without hand writing raw script.

OP_VERIFY vs OP_RETURN

OP_VERIFY and OP_RETURN both end a script badly, but only one of them asks a question first. OP_VERIFY fails when the value handed to it is false, so a correct spend runs straight past it without leaving a trace. OP_RETURN, byte 0x6a, fails whenever it is executed, with no condition attached at all, which is precisely why it is used to build outputs that can never be spent and to attach data to a transaction. A script that dies on the first was presented with something wrong. A script that dies on the second was never meant to be spendable to begin with.

Not to be confused with

Frequently asked questions

What counts as true inside Bitcoin Script?

Anything that is not an empty item, not a string of zero bytes, and not negative zero. A hash, a public key or the single byte 0x01 all count as true, and the interpreter never looks at what the bytes were supposed to mean.

Why do OP_CHECKLOCKTIMEVERIFY and OP_CHECKSEQUENCEVERIFY leave the stack alone?

Because they were added by soft fork onto opcodes that older nodes treat as doing nothing. Consuming an item would have made those nodes disagree about the stack, so the new opcodes only inspect it and scripts add OP_DROP afterwards.

Related terms

More in Developer reference