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:
| Feature | Bitcoin Script | Why |
|---|---|---|
| Turing-complete | No | Prevents infinite loops |
| Loops | No | Predictable execution time |
| State | No | Stateless verification |
| Complexity | Low | Reduces 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
| Opcode | Effect |
|---|---|
| OP_DUP | Duplicate top item |
| OP_DROP | Remove top item |
| OP_SWAP | Swap top two items |
| OP_ROT | Rotate top three items |
Arithmetic
| Opcode | Effect |
|---|---|
| OP_ADD | Add top two items |
| OP_SUB | Subtract |
| OP_1ADD | Add 1 |
| OP_NEGATE | Negate top item |
Crypto
| Opcode | Effect |
|---|---|
| OP_SHA256 | SHA256 hash |
| OP_HASH160 | SHA256 then RIPEMD160 |
| OP_CHECKSIG | Verify signature |
| OP_CHECKMULTISIG | Verify m-of-n signatures |
Flow Control
| Opcode | Effect |
|---|---|
| OP_IF | If top is true, execute |
| OP_ELSE | Else branch |
| OP_ENDIF | End if block |
| OP_VERIFY | Fail if top is false |
Time Locks
| Opcode | Effect |
|---|---|
| OP_CHECKLOCKTIMEVERIFY | Block height/time lock |
| OP_CHECKSEQUENCEVERIFY | Relative 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?
- Predictable execution: Know max resource usage
- No halting problem: Script always terminates
- Simpler security analysis: Easier to audit
- DoS prevention: Can't create infinite loops
SegWit: Script Evolution
Segregated Witness (2017) moved signatures outside the main transaction.
Benefits
- Fix malleability: Signatures don't affect txid
- Lower fees: Witness data discounted
- Better upgrade path: Versioned witness programs
- 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
- Script defines spending conditions — a lock on every output
- Stack-based and limited — security through simplicity
- Not Turing-complete by design — prevents DoS and complexity
- Evolution: P2PKH → P2SH → SegWit → Taproot
- Enables complex contracts — multisig, HTLCs, time locks
- Taproot improves privacy — all transactions look similar
Questions to Consider
- Why were some opcodes disabled?
- Could Bitcoin have a more expressive scripting language?
- How does Script compare to Ethereum's EVM?
- What new capabilities does Taproot enable?