How Scripts Are Evaluated Step by Step
How Scripts Are Evaluated Step by Step
Understanding the step-by-step evaluation process of Bitcoin Script is essential for anyone writing, debugging, or auditing Bitcoin transactions. The process is deterministic, meaning the same script will always produce the same result on any node in the world.
The Evaluation Pipeline
When a Bitcoin node receives a transaction, it validates each input by:
-
Retrieving the locking script from the previous output being spent
-
Taking the unlocking script from the current input
-
Running the unlocking script to build an initial stack state
-
Running the locking script against that stack
-
Checking whether the final stack top is non-zero (TRUE)
For SegWit inputs (P2WPKH, P2WSH, P2TR), this process is modified — the witness data replaces or supplements the scriptSig.
Phase 1: Deserialization
Before execution, the raw script bytes must be deserialized into a sequence of opcodes and data push items. This is called script parsing.
Raw hex: 76a914{20-byte-hash}88ac
Parsed:
0x76 → OP_DUP
0xa9 → OP_HASH160
0x14 → push 20 bytes
{20 bytes} → <pubKeyHash>
0x88 → OP_EQUALVERIFY
0xac → OP_CHECKSIG
Phase 2: Unlocking Script Execution
The interpreter executes the unlocking script (scriptSig) first. For P2PKH, this pushes the signature and public key onto the stack.
ScriptSig bytes → <sig_length><sig><pubKey_length><pubKey>
After execution:
Stack: [sig, pubKey]
For P2SH transactions, the unlocking script also pushes the serialized redeem script as its final item. This redeem script is later deserialized and executed separately.
Phase 3: Locking Script Execution
The interpreter then runs the locking script (scriptPubKey) against the stack left by the unlocking script. Each opcode is processed in order.
Script: OP_DUP OP_HASH160 <hash> OP_EQUALVERIFY OP_CHECKSIG
Step 1: OP_DUP
Before: [sig, pubKey]
After: [sig, pubKey, pubKey]
Step 2: OP_HASH160
Before: [sig, pubKey, pubKey]
After: [sig, pubKey, HASH160(pubKey)]
Step 3: PUSH
Before: [sig, pubKey, HASH160(pubKey)]
After: [sig, pubKey, HASH160(pubKey), expectedHash]
Step 4: OP_EQUALVERIFY
Before: [sig, pubKey, HASH160(pubKey), expectedHash]
After: [sig, pubKey] ← if equal; script fails if not equal
Step 5: OP_CHECKSIG
Before: [sig, pubKey]
After: [1] ← if signature valid; [0] if invalid
Phase 4: Final Stack Check
After both scripts have executed, the interpreter checks the top of the stack:
-
If the top item is non-zero (including
OP_1, any positive number, non-empty byte array): VALID -
If the top item is zero, an empty byte array, or the stack is empty: INVALID
# Pseudocode for final validation
def validate_script(unlocking_script, locking_script):
stack = execute(unlocking_script)
stack = execute(locking_script, stack)
if not stack:
return False # Empty stack = fail
top = stack[-1]
return top != 0 and top != b'' # Non-zero = success
Failure Modes
A script can fail in several ways:
-
OP_VERIFY fails: The top item was false. Script terminates immediately.
-
Stack underflow: An opcode needed items that weren't there.
-
Stack overflow: More than 1,000 items accumulated.
-
Disabled opcode: A banned opcode (like
OP_CAT) was encountered. -
Invalid encoding: A push operation claimed to push more bytes than exist.
-
Signature check fails:
OP_CHECKSIGpushed0instead of1.
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: