TeachMeBitcoin

How Scripts Are Evaluated Step by Step

From TeachMeBitcoin, the free encyclopedia Reading time: 3 min

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:

  1. Retrieving the locking script from the previous output being spent

  2. Taking the unlocking script from the current input

  3. Running the unlocking script to build an initial stack state

  4. Running the locking script against that stack

  5. 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:

# 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:

☕ Help support TeachMeBitcoin

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:

Ethereum: 0x578417C51783663D8A6A811B3544E1f779D39A85
Bitcoin: bc1q77k9e95rn669kpzyjr8ke9w95zhk7pa5s63qzz
Solana: 4ycT2ayqeMucixj3wS8Ay8Tq9NRDYRPKYbj3UGESyQ4J
Address copied to clipboard!