Bitcoin Deep Dive

Bitcoin Script

Bitcoin Script

Module 3 of Bitcoin Deep Dive


What Is Bitcoin Script?

Bitcoin Script is the programming language that defines how bitcoins can be spent. Every UTXO has a "locking script" that specifies the conditions for spending.

Think of it as a lock (on the output) that requires the right key (in the input) to open.


Design Philosophy

Bitcoin Script is intentionally limited:

FeatureBitcoin ScriptWhy
Turing-completeNoPrevents infinite loops
LoopsNoPredictable execution time
StateNoStateless verification
ComplexityLowReduces attack surface

"Bitcoin Script is designed to be simple enough that any valid script will execute in a bounded amount of time."


Stack-Based Execution

Script uses a stack — a Last-In-First-Out (LIFO) data structure.

Operations:
PUSH: Add item to top of stack
POP: Remove item from top
Operations work on top items

Example: 2 3 OP_ADD
Stack: []
Push 2: [2]
Push 3: [2, 3]
OP_ADD: Pop 3, Pop 2, Push 5 → [5]

How Scripts Work

Locking Script (scriptPubKey)

Placed on the output. Defines conditions to spend.

Example: "Provide signature matching public key X"

Unlocking Script (scriptSig)

Placed on the input. Provides data to satisfy locking script.

Example: "<signature>"

Verification

Concatenate and execute:

<unlocking script> + <locking script>

If stack ends with TRUE (non-zero), transaction is valid.

Common Script Types

1. P2PKH (Pay-to-Public-Key-Hash)

The original standard. Most common historically.

Locking Script:

OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

Unlocking Script:

<signature> <publicKey>

Execution:

1. <sig> <pubKey>                    - Push signature and public key
2. OP_DUP                            - Duplicate public key
3. OP_HASH160                        - Hash the public key
4. <pubKeyHash>                      - Push expected hash
5. OP_EQUALVERIFY                    - Verify hashes match
6. OP_CHECKSIG                       - Verify signature

2. P2SH (Pay-to-Script-Hash)

Hide complex script behind a hash. Introduced in BIP 16.

Use case: Multisig, time locks, complex conditions

Locking Script:

OP_HASH160 <scriptHash> OP_EQUAL

Unlocking Script:

<data> <serializedScript>

Benefit: Sender only sees a hash. Complexity is receiver's responsibility.

3. P2WPKH (Native SegWit)

Modern standard. Lower fees, better security.

Address format: Starts with "bc1q..."

Locking Script:

OP_0 <20-byte-pubKeyHash>

Witness data (separate from transaction):

<signature> <publicKey>

4. P2TR (Taproot)

Latest upgrade (2021). Best privacy and efficiency.

Address format: Starts with "bc1p..."

Features:

  • Key path: Spend with single signature (looks like any other)
  • Script path: Complex conditions hidden unless used
  • Better privacy: All spends look similar

Useful Opcodes

Stack Operations

OpcodeEffect
OP_DUPDuplicate top item
OP_DROPRemove top item
OP_SWAPSwap top two items
OP_ROTRotate top three items

Arithmetic

OpcodeEffect
OP_ADDAdd top two items
OP_SUBSubtract
OP_1ADDAdd 1
OP_NEGATENegate top item

Crypto

OpcodeEffect
OP_SHA256SHA256 hash
OP_HASH160SHA256 then RIPEMD160
OP_CHECKSIGVerify signature
OP_CHECKMULTISIGVerify m-of-n signatures

Flow Control

OpcodeEffect
OP_IFIf top is true, execute
OP_ELSEElse branch
OP_ENDIFEnd if block
OP_VERIFYFail if top is false

Time Locks

OpcodeEffect
OP_CHECKLOCKTIMEVERIFYBlock height/time lock
OP_CHECKSEQUENCEVERIFYRelative time lock

Advanced Scripts

Multisig (m-of-n)

Require m signatures from n possible keys.

2-of-3 Multisig:

Locking: OP_2 <pubK1> <pubK2> <pubK3> OP_3 OP_CHECKMULTISIG
Unlocking: OP_0 <sig1> <sig2>

Use cases: Corporate treasuries, escrow, family funds

Hash Time-Locked Contract (HTLC)

Foundation of Lightning Network.

IF
  OP_HASH160 <hash> OP_EQUALVERIFY
  <recipientPubKey> OP_CHECKSIG
ELSE
  <timeout> OP_CHECKLOCKTIMEVERIFY OP_DROP
  <senderPubKey> OP_CHECKSIG
ENDIF

Meaning:

  • Recipient can claim with preimage of hash
  • OR sender can reclaim after timeout

Time-Locked Savings

<futureTime> OP_CHECKLOCKTIMEVERIFY OP_DROP
<pubKey> OP_CHECKSIG

Can't spend until specified time. Enforced by consensus!


Script Limitations

Disabled Opcodes

Some opcodes were disabled for security:

  • OP_CAT (concatenate)
  • OP_SUBSTR (substring)
  • OP_MUL (multiply)
  • OP_DIV (divide)

These could enable DoS attacks or unexpected behaviors.

Why Not Turing-Complete?

  1. Predictable execution: Know max resource usage
  2. No halting problem: Script always terminates
  3. Simpler security analysis: Easier to audit
  4. DoS prevention: Can't create infinite loops

SegWit: Script Evolution

Segregated Witness (2017) moved signatures outside the main transaction.

Benefits

  1. Fix malleability: Signatures don't affect txid
  2. Lower fees: Witness data discounted
  3. Better upgrade path: Versioned witness programs
  4. Enable Lightning: Required for payment channels

Weight Units

Transaction weight = Base size × 4 + Witness size
Block limit: 4 million weight units

Witness data only counts 1/4 toward limit!


Taproot: The Latest Upgrade

Activated November 2021 via Schnorr signatures + MAST.

Key Innovations

1. Schnorr Signatures

  • More efficient than ECDSA
  • Native multisig (n signatures → 1 signature)
  • Better privacy

2. MAST (Merkelized Abstract Syntax Trees) Only reveal the script branch you use.

Traditional: Reveal entire script
MAST: Reveal only executed branch + Merkle proof

Script has 10 conditions?
Only reveal the 1 condition used.

3. Key Path vs Script Path

  • Key path: Single signature (most common case)
  • Script path: Complex script (fallback)

Both look identical on-chain until script path is used!


Key Takeaways

  1. Script defines spending conditions — a lock on every output
  2. Stack-based and limited — security through simplicity
  3. Not Turing-complete by design — prevents DoS and complexity
  4. Evolution: P2PKH → P2SH → SegWit → Taproot
  5. Enables complex contracts — multisig, HTLCs, time locks
  6. Taproot improves privacy — all transactions look similar

Questions to Consider

  1. Why were some opcodes disabled?
  2. Could Bitcoin have a more expressive scripting language?
  3. How does Script compare to Ethereum's EVM?
  4. What new capabilities does Taproot enable?