How Data Items Sit on the Stack
10. How Data Items Sit on the Stack
Overview
The Bitcoin Script execution environment is built around a last-in, first-out (LIFO) stack. Understanding precisely how data items are stored on this stack — their representation, size limits, ordering conventions, and consumption patterns — is fundamental to writing, analyzing, and debugging Script programs.
Stack Architecture
Bitcoin Script uses two stacks during execution:
Main stack: primary execution stack, used by all opcodes
Alt stack: auxiliary stack, used only by OP_TOALTSTACK / OP_FROMALTSTACK
Both stacks: LIFO (last in, first out)
Max stack depth: 1000 items (consensus limit)
Each stack slot holds a valtype, which in Bitcoin Core is defined as:
// From src/script/script.h
typedef std::vector<unsigned char> valtype;
A valtype is simply a byte vector — an ordered sequence of bytes with no inherent type information. Script opcodes impose their own interpretation (as integer, boolean, signature bytes, etc.) when they consume stack items.
How Items Are Ordered
Items are pushed onto the top of the stack. Opcodes reference items by their position relative to the top:
Stack state after pushing A, B, C (A first, C last):
Top --> C (stacktop(-1), most recently pushed)
-- B (stacktop(-2))
Bottom --> A (stacktop(-3))
In Bitcoin Core source code, the top of the stack is accessed as stacktop(-1):
// Stack access macros from src/script/interpreter.cpp
#define stacktop(i) (stack.at(stack.size()+(i)))
// stacktop(-1) is top, stacktop(-2) is one below top, etc.
Size Limits on Stack Items
Maximum bytes per stack element: 520 bytes
(MAX_SCRIPT_ELEMENT_SIZE in script.h)
Maximum number of items on either stack: 1000
(MAX_STACK_SIZE in interpreter.cpp)
Attempting to push a 521-byte item onto the stack is a consensus error. Attempting any operation that would cause either stack to exceed 1000 items is also a consensus error.
Integer Representation on Stack
When Script arithmetic opcodes push integers, they use the sign-magnitude little-endian encoding:
Integer 0: [] (empty vector)
Integer 1: [0x01]
Integer -1: [0x81]
Integer 127: [0x7f]
Integer 128: [0x80, 0x00]
Integer -128: [0x80, 0x80]
Integer 256: [0x00, 0x01]
Integer -256: [0x00, 0x81]
Arithmetic opcodes like OP_ADD, OP_SUB, OP_MUL interpret stack items as CScriptNum values. They enforce a maximum integer width of 4 bytes (values in range -2^31 + 1 to 2^31 - 1), except when specific opcodes extend this (e.g., OP_CHECKSEQUENCEVERIFY uses 5-byte integers).
Boolean Representation on Stack
Boolean evaluation follows these rules applied to any stack item:
Item evaluates to FALSE if:
- It is an empty vector []
- It consists entirely of 0x00 bytes: [0x00], [0x00, 0x00], etc.
- It is a negative zero: [0x80], [0x00, 0x80], etc.
Item evaluates to TRUE if:
- Any other value
- Non-zero byte present where it matters
Examples:
[] --> false
[0x00] --> false
[0x80] --> false (negative zero)
[0x00, 0x80] --> false (negative zero, 2 bytes)
[0x01] --> true
[0xFF] --> true
[0x00, 0x01] --> true
Data Flow Example: P2PKH
scriptSig: <sig> <pubkey>
scriptPubKey: OP_DUP OP_HASH160 <pubkey_hash> OP_EQUALVERIFY OP_CHECKSIG
Combined execution:
Initial stack: []
Push sig: [<sig>]
Push pubkey: [<sig>, <pubkey>]
OP_DUP: [<sig>, <pubkey>, <pubkey>]
OP_HASH160: [<sig>, <pubkey>, HASH160(<pubkey>)]
Push hash: [<sig>, <pubkey>, HASH160(<pubkey>), <pubkey_hash>]
OP_EQUALVERIFY:[<sig>, <pubkey>] (pops two, verifies equal, leaves nothing)
OP_CHECKSIG: [0x01] (pops sig+pubkey, pushes true if valid)
Final stack: [0x01] --> script succeeds (top item is true)
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: