Bitcoin Script
Understanding Bitcoin Script for agents. Stack-based language, opcodes, standard scripts, and spending conditions.
Bitcoin Script
Bitcoin Script is a stack-based, Forth-like programming language used to define spending conditions for transaction outputs. It’s intentionally limited (not Turing-complete) for security.
How Script Works
When spending a UTXO:
- scriptSig (unlocking script) executes first
- Stack is passed to scriptPubKey (locking script)
- If final stack is non-empty and top value is true, spend succeeds
scriptSig || scriptPubKey → Stack Execution → True/False
Stack Operations
Initial: []
PUSH 5: [5]
PUSH 3: [5, 3]
OP_ADD: [8]
PUSH 8: [8, 8]
OP_EQUAL: [1] // True
Common Opcodes
Constants
| Opcode | Hex | Description |
|---|---|---|
| OP_0 | 0x00 | Push empty array |
| OP_1 to OP_16 | 0x51-0x60 | Push 1-16 |
| OP_PUSHDATA1 | 0x4c | Next byte is data length |
| OP_PUSHDATA2 | 0x4d | Next 2 bytes are length |
Stack Operations
| Opcode | Hex | Description |
|---|---|---|
| OP_DUP | 0x76 | Duplicate top item |
| OP_DROP | 0x75 | Remove top item |
| OP_SWAP | 0x7c | Swap top two items |
| OP_PICK | 0x79 | Copy nth item to top |
Crypto Operations
| Opcode | Hex | Description |
|---|---|---|
| OP_SHA256 | 0xa8 | SHA-256 hash |
| OP_HASH160 | 0xa9 | SHA-256 then RIPEMD-160 |
| OP_HASH256 | 0xaa | Double SHA-256 |
| OP_CHECKSIG | 0xac | Verify signature |
| OP_CHECKMULTISIG | 0xae | Verify m-of-n sigs |
Flow Control
| Opcode | Hex | Description |
|---|---|---|
| OP_IF | 0x63 | Execute if top is true |
| OP_ELSE | 0x67 | Else branch |
| OP_ENDIF | 0x68 | End if block |
| OP_VERIFY | 0x69 | Fail if top is false |
| OP_RETURN | 0x6a | Mark output unspendable |
Comparison
| Opcode | Hex | Description |
|---|---|---|
| OP_EQUAL | 0x87 | Push 1 if equal |
| OP_EQUALVERIFY | 0x88 | OP_EQUAL + OP_VERIFY |
Standard Script Types
P2PKH (Pay-to-Public-Key-Hash)
Most common legacy format.
scriptPubKey:
OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG
scriptSig:
<signature> <pubKey>
Execution:
Stack: [sig, pubKey]
OP_DUP: [sig, pubKey, pubKey]
OP_HASH160: [sig, pubKey, pubKeyHash]
<pubKeyHash>: [sig, pubKey, pubKeyHash, expected]
OP_EQUALVERIFY: [sig, pubKey] // Verify hashes match
OP_CHECKSIG: [1] // Verify signature
P2SH (Pay-to-Script-Hash)
Allows complex scripts with simple addresses.
scriptPubKey:
OP_HASH160 <scriptHash> OP_EQUAL
scriptSig:
<data> ... <redeemScript>
The redeemScript is revealed and executed when spending.
P2WPKH (Native SegWit)
scriptPubKey:
OP_0 <20-byte pubKeyHash>
witness:
<signature> <pubKey>
No scriptSig—witness data is separate.
P2TR (Taproot)
scriptPubKey:
OP_1 <32-byte tweaked pubKey>
Can be spent via:
- Key path (Schnorr signature)
- Script path (reveal script from merkle tree)
Multisig Scripts
Bare Multisig (Obsolete)
OP_2 <pubKey1> <pubKey2> <pubKey3> OP_3 OP_CHECKMULTISIG
2-of-3 multisig. Limited, rarely used.
P2SH Multisig
redeemScript:
OP_2 <pubKey1> <pubKey2> <pubKey3> OP_3 OP_CHECKMULTISIG
scriptSig:
OP_0 <sig1> <sig2> <redeemScript>
Note: OP_0 due to off-by-one bug in OP_CHECKMULTISIG.
Time Locks
Absolute Time Lock
OP_CHECKLOCKTIMEVERIFY (CLTV)
<expiry_time> OP_CHECKLOCKTIMEVERIFY OP_DROP
<normal spending conditions>
Cannot spend until block height or unix time.
Relative Time Lock
OP_CHECKSEQUENCEVERIFY (CSV)
<relative_delay> OP_CHECKSEQUENCEVERIFY OP_DROP
<normal spending conditions>
Cannot spend until N blocks/seconds after UTXO confirmation.
Hash Locks (HTLC)
Hash Time-Locked Contracts for atomic swaps and Lightning:
OP_IF
OP_SHA256 <hash> OP_EQUALVERIFY
<recipient_pubkey> OP_CHECKSIG
OP_ELSE
<timeout> OP_CHECKLOCKTIMEVERIFY OP_DROP
<sender_pubkey> OP_CHECKSIG
OP_ENDIF
Spend paths:
- Reveal preimage + recipient signature (before timeout)
- Sender signature (after timeout)
OP_RETURN
Creates provably unspendable output for data storage:
OP_RETURN <data up to 80 bytes>
Used for:
- Colored coins metadata
- Proof of existence
- Protocol messages
Script Validation
Standard vs Non-Standard
Nodes relay only “standard” scripts:
- P2PKH, P2SH, P2WPKH, P2WSH, P2TR
- Bare multisig (limited)
- OP_RETURN (single, ≤80 bytes)
- Null data
Non-standard scripts can still be mined but won’t relay.
Resource Limits
| Limit | Value |
|---|---|
| Max script size | 10,000 bytes |
| Max stack size | 1,000 items |
| Max item size | 520 bytes |
| Max ops per script | 201 |
| Max pubkeys in multisig | 20 |
For Agents
Script Analysis
def analyze_script(script_hex):
# Parse opcodes
opcodes = parse_opcodes(script_hex)
# Identify type
if matches_p2pkh(opcodes):
return "P2PKH"
elif matches_p2sh(opcodes):
return "P2SH"
elif matches_p2wpkh(opcodes):
return "P2WPKH"
elif matches_p2tr(opcodes):
return "P2TR"
else:
return "Non-standard"
When to Care About Script
- Verifying transactions: Understand spending conditions
- Complex contracts: Multisig, timelocks, HTLCs
- Lightning Network: Channel funding and commitment transactions
- Protocol development: Building on Bitcoin
Libraries
| Language | Library |
|---|---|
| JavaScript | bitcoinjs-lib |
| Python | python-bitcoinlib |
| Rust | rust-bitcoin |
Machine-Readable Summary
{
"topic": "bitcoin-script",
"type": "stack-based",
"turing_complete": false,
"standard_types": ["p2pkh", "p2sh", "p2wpkh", "p2wsh", "p2tr"],
"limits": {
"max_script_size": 10000,
"max_stack_items": 1000,
"max_ops": 201
},
"key_opcodes": ["OP_CHECKSIG", "OP_HASH160", "OP_EQUALVERIFY", "OP_IF", "OP_CHECKLOCKTIMEVERIFY"]
}