TeachMeBitcoin

How Data Items Sit on the Stack

From TeachMeBitcoin, the free encyclopedia Reading time: 3 min

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)

☕ Help support TeachMeBitcoin

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:

Ethereum: 0x578417C51783663D8A6A811B3544E1f779D39A85
Bitcoin: bc1q77k9e95rn669kpzyjr8ke9w95zhk7pa5s63qzz
Solana: 4ycT2ayqeMucixj3wS8Ay8Tq9NRDYRPKYbj3UGESyQ4J
Address copied to clipboard!