Bitcoin transactions do not simply transfer value from one public key to another. Instead, they lock value to programs written in a simple stack-based language called Script. Understanding Script is essential for comprehending how Bitcoin enforces spending conditions and enables advanced features like multisignature wallets and time-locked contracts.
Script is intentionally limited: it is not Turing-complete, has no loops, and every program terminates in bounded time. These restrictions make transaction validation predictable and prevent denial-of-service attacks.
11.1 The Execution Model
Script programs operate on two stacks: the main stack and an auxiliary "alt" stack. Execution proceeds by reading opcodes (operation codes) and data from the script, manipulating the stacks accordingly.
Definition 11.1 (Script Execution)
A script is a sequence of opcodes and data pushes. Execution:
- Begins with an empty main stack
- Processes each element left-to-right
- Data elements are pushed onto the stack
- Opcodes consume and produce stack elements
- Succeeds if the final stack is non-empty and the top element is true (nonzero)
11.1.1 Script Concatenation
To validate a transaction input, the scriptSig (unlocking script) is concatenated with the scriptPubKey (locking script) of the referenced output, then executed.
Definition 11.2 (Script Validation)
For legacy (pre-SegWit) transactions, validation executes:
scriptSig || scriptPubKey
The transaction input is valid if and only if execution succeeds and leaves a true value on the stack.
Remark 11.1 (Separate Execution)
Historically, scriptSig and scriptPubKey were concatenated and run together. Due to security concerns (malicious scriptSigs could manipulate the stack before scriptPubKey runs), modern implementations execute them separately: scriptSig first, then scriptPubKey operates on the resulting stack.
11.2 Data Push Operations
Non-opcode bytes in a script represent data to be pushed onto the stack. The length of the data determines the push opcode.
| Opcode Range | Name | Effect |
|---|---|---|
0x00 |
OP_0 / OP_FALSE | Push empty byte array (falsy) |
0x01–0x4b |
(direct push) | Push next N bytes (N = opcode value) |
0x4c |
OP_PUSHDATA1 | Next byte is length; push that many bytes |
0x4d |
OP_PUSHDATA2 | Next 2 bytes are length (little-endian) |
0x4e |
OP_PUSHDATA4 | Next 4 bytes are length (little-endian) |
0x4f |
OP_1NEGATE | Push the number −1 |
0x51–0x60 |
OP_1 through OP_16 | Push the number 1–16 |
Example 11.1 (Data Push)
To push the 20-byte hash 89abcdef...12345678:
14 89abcdef...12345678 ↑ └──────────────────┘ │ 20 bytes └─ opcode 0x14 = 20 (push 20 bytes)
11.3 Stack Manipulation Opcodes
These opcodes rearrange elements on the stack without performing computation.
| Opcode | Name | Effect |
|---|---|---|
0x76 |
OP_DUP | Duplicate top element |
0x75 |
OP_DROP | Remove top element |
0x7c |
OP_SWAP | Swap top two elements |
0x77 |
OP_NIP | Remove second element |
0x78 |
OP_OVER | Copy second element to top |
0x79 |
OP_PICK | Copy element at depth n to top |
0x7a |
OP_ROLL | Move element at depth n to top |
0x73 |
OP_IFDUP | Duplicate top if nonzero |
0x74 |
OP_DEPTH | Push stack depth |
11.4 Arithmetic Opcodes
Script supports limited arithmetic on 32-bit signed integers.
| Opcode | Name | Effect |
|---|---|---|
0x93 |
OP_ADD | a + b |
0x94 |
OP_SUB | a − b |
0x8b |
OP_1ADD | a + 1 |
0x8c |
OP_1SUB | a − 1 |
0x8f |
OP_NEGATE | −a |
0x90 |
OP_ABS | |a| |
0x9a |
OP_MIN | min(a, b) |
0x9b |
OP_MAX | max(a, b) |
0xa0 |
OP_WITHIN | min ≤ x < max |
Remark 11.2 (Disabled Opcodes)
OP_MUL, OP_DIV, OP_MOD, and bitwise
operations were disabled early in Bitcoin's history due to potential
implementation bugs. They remain reserved but cause script failure if encountered.
11.5 Cryptographic Opcodes
The most important opcodes perform cryptographic operations.
11.5.1 Hash Functions
| Opcode | Name | Effect |
|---|---|---|
0xa9 |
OP_HASH160 | RIPEMD160(SHA256(top)) |
0xaa |
OP_HASH256 | SHA256(SHA256(top)) |
0xa7 |
OP_SHA1 | SHA1(top) |
0xa8 |
OP_SHA256 | SHA256(top) |
0xa6 |
OP_RIPEMD160 | RIPEMD160(top) |
11.5.2 Signature Verification
| Opcode | Name | Effect |
|---|---|---|
0xac |
OP_CHECKSIG | Verify signature against public key |
0xad |
OP_CHECKSIGVERIFY | OP_CHECKSIG + OP_VERIFY |
0xae |
OP_CHECKMULTISIG | Verify m-of-n multisignature |
0xaf |
OP_CHECKMULTISIGVERIFY | OP_CHECKMULTISIG + OP_VERIFY |
Definition 11.3 (OP_CHECKSIG)
OP_CHECKSIG pops a public key and signature from the stack.
It verifies that the signature is valid for the transaction (as determined
by the sighash type) under that public key. It pushes OP_TRUE
(1) on success or OP_FALSE (empty array) on failure.
11.6 Flow Control
Script supports conditional execution (but not loops).
| Opcode | Name | Effect |
|---|---|---|
0x63 |
OP_IF | Execute following if top is true |
0x64 |
OP_NOTIF | Execute following if top is false |
0x67 |
OP_ELSE | Alternative branch |
0x68 |
OP_ENDIF | End conditional block |
0x69 |
OP_VERIFY | Fail if top is false, else remove it |
0x6a |
OP_RETURN | Immediately fail (marks output unspendable) |
Example 11.2 (Conditional Script)
OP_IF
<pubkey_A>
OP_ELSE
<pubkey_B>
OP_ENDIF
OP_CHECKSIG
This script allows spending with either key A (if the condition is true) or key B (if false). The spender provides a boolean value before their signature.
11.7 Time Lock Opcodes
BIP-65 and BIP-112 introduced opcodes for time-based spending conditions.
| Opcode | Name | BIP | Effect |
|---|---|---|---|
0xb1 |
OP_CHECKLOCKTIMEVERIFY | BIP-65 | Fail if current time/height < top of stack |
0xb2 |
OP_CHECKSEQUENCEVERIFY | BIP-112 | Fail if relative time since UTXO creation < top |
Example 11.3 (Time-Locked Output)
An output that can only be spent after block 800,000:
<800000> OP_CHECKLOCKTIMEVERIFY OP_DROP OP_DUP OP_HASH160 <pubkey_hash> OP_EQUALVERIFY OP_CHECKSIG
The OP_DROP removes the lock time from the stack after verification,
then standard P2PKH logic follows.
11.8 Standard Script Templates
While Script allows arbitrary programs, nodes relay only "standard" transactions using recognized templates.
11.8.1 P2PKH (Pay-to-Public-Key-Hash)
Definition 11.4 (P2PKH Scripts)
ScriptPubKey (locking):
OP_DUP OP_HASH160 <20-byte-hash> OP_EQUALVERIFY OP_CHECKSIG
ScriptSig (unlocking):
<signature> <pubkey>
11.8.2 P2SH (Pay-to-Script-Hash)
Definition 11.5 (P2SH Execution)
P2SH scripts are evaluated in two phases:
- Verify that the provided redeem script hashes to the expected value
- Execute the redeem script with the remaining stack elements
ScriptPubKey:
OP_HASH160 <20-byte-hash> OP_EQUAL
ScriptSig:
<...signatures...> <redeem_script>
11.8.3 Multisignature
Definition 11.6 (m-of-n Multisig)
An m-of-n multisignature script requires m valid signatures from a set of n public keys:
OP_m <pubkey_1> ... <pubkey_n> OP_n OP_CHECKMULTISIG
Remark 11.3 (Off-by-One Bug)
OP_CHECKMULTISIG has a historical bug: it pops one extra element
from the stack beyond what's needed. The scriptSig must include a dummy
OP_0 at the beginning to account for this.
11.9 OP_RETURN and Data Embedding
OP_RETURN creates a provably unspendable output, useful for
embedding arbitrary data in the blockchain.
Definition 11.7 (OP_RETURN Output)
An OP_RETURN output has a scriptPubKey beginning with
OP_RETURN followed by up to 80 bytes of data. Such outputs:
- Are provably unspendable (execution always fails)
- Can be pruned from the UTXO set
- Should have zero value
Example 11.4 (Data Embedding)
To embed the text "Hello, Bitcoin!" in the blockchain:
OP_RETURN 48656c6c6f2c20426974636f696e21
└─────────────────────────────┘
"Hello, Bitcoin!" in hex
11.10 SegWit Script Execution
Native SegWit outputs use a different execution model. The scriptPubKey is a "witness program," and the actual unlocking data is in the witness field.
Definition 11.8 (Witness Program)
A witness program is a scriptPubKey of the form:
<version> <program>
where version is OP_0 through OP_16, and program is 2–40 bytes.
| Version | Program Length | Interpretation |
|---|---|---|
| 0 | 20 bytes | P2WPKH: witness = [sig, pubkey] |
| 0 | 32 bytes | P2WSH: witness = [..., witness_script] |
| 1 | 32 bytes | P2TR: Taproot (Schnorr or Tapscript) |
11.11 Tapscript
Taproot (BIP-341, BIP-342) introduces Tapscript, an upgraded Script with several improvements.
Definition 11.9 (Tapscript Changes)
Tapscript differs from legacy Script:
OP_CHECKSIGuses Schnorr signatures (BIP-340)OP_CHECKMULTISIGis disabled; useOP_CHECKSIGADDinsteadOP_SUCCESSopcodes allow future soft-fork upgrades- Signature validation is more efficient (batch verification possible)
Example 11.5 (Tapscript Multisig)
A 2-of-3 multisig in Tapscript:
<pubkey_1> OP_CHECKSIG <pubkey_2> OP_CHECKSIGADD <pubkey_3> OP_CHECKSIGADD OP_2 OP_NUMEQUAL
Each OP_CHECKSIGADD adds 1 to a running counter if the signature
is valid, enabling clean m-of-n without the legacy off-by-one bug.
11.12 Script Limitations
Script's restrictions are intentional, ensuring predictable validation.
Theorem 11.1 (Script Termination)
Every valid Script program terminates. The maximum number of operations is bounded by:
- Maximum script size: 10,000 bytes
- Maximum stack size: 1,000 elements
- Maximum element size: 520 bytes
- Maximum opcode count: 201 (non-push operations)
These limits ensure that script validation has bounded time and space complexity, preventing denial-of-service attacks.
Exercises
Exercise 11.1
Trace the execution of the following script with initial stack [3, 5]:
OP_ADD OP_6 OP_EQUAL
Does execution succeed or fail?
Exercise 11.2
Write a script that allows spending if the spender provides two numbers whose product is 42. (Note: OP_MUL is disabled, so you'll need a workaround.)
Exercise 11.3
Explain why Script is not Turing-complete. What feature would need to be added to make it Turing-complete, and why would this be problematic?
Exercise 11.4
A P2SH output encodes a 2-of-3 multisig. Write out both the scriptPubKey and a valid scriptSig (with placeholder signatures).
Exercise 11.5
Why is OP_RETURN important for the UTXO set? What would happen if users embedded data in spendable outputs instead?