Glossary

Script Number Format (CScriptNum)

CScriptNum is Bitcoin Script's signed integer encoding, using little-endian byte order with a sign bit in the most significant byte.

Key Takeaways

  • CScriptNum is the variable-length integer encoding used by Bitcoin Script: it stores numbers as little-endian sign-magnitude bytes rather than the two's complement format used by most CPUs.
  • The sign bit occupies bit 7 (0x80) of the last byte, zero is represented as an empty byte array, and a minimal encoding rule rejects any non-canonical representation to prevent transaction malleability.
  • Numeric opcodes enforce a default 4-byte limit (roughly ±2.1 billion), with OP_CHECKLOCKTIMEVERIFY being a notable exception that accepts 5-byte values.

What Is the Script Number Format (CScriptNum)?

The Script Number Format, implemented as the CScriptNum class in Bitcoin Core, is the way Bitcoin Script represents signed integers on its execution stack. Any time an opcode such as OP_CHECKLOCKTIMEVERIFY, OP_CHECKSEQUENCEVERIFY, OP_ADD, or OP_EQUAL interprets a stack element as a number, it uses CScriptNum to decode and validate the raw bytes.

Unlike most modern hardware, which uses two's complement to represent negative numbers, Bitcoin Script uses a sign-magnitude scheme with little-endian byte order. This encoding was chosen in Bitcoin's original design and has remained unchanged since the genesis block. Understanding CScriptNum is essential for anyone writing or auditing spending conditions, because an incorrectly encoded number can make a transaction invalid even if the underlying logic is correct.

How It Works

CScriptNum encodes a signed integer as a variable-length byte array following three rules: little-endian byte order, a sign bit in the most significant byte, and a strict minimality requirement.

Little-Endian Byte Order

The least significant byte comes first. For example, the integer 256 (0x0100 in big-endian hexadecimal) is stored as [0x00, 0x01] with the low byte at position zero.

Sign-Magnitude Representation

The sign of the number is encoded in the top bit (bit 7, mask 0x80) of the last byte. If that bit is set, the number is negative. The remaining bits carry the magnitude. This differs from two's complement, where negation involves flipping all bits and adding one:

  • In two's complement, -1 is 0xFF (all bits set) for an 8-bit value
  • In CScriptNum, -1 is [0x81]: the magnitude 1 (0x01) with the sign bit (0x80) set in the same byte

When the magnitude of a value would set bit 7 of the last byte, an extra byte is appended to carry the sign. For instance, 128 cannot be stored as [0x80] because that would be interpreted as -0. Instead, 128 is encoded as [0x80, 0x00]: the value byte followed by a zero byte whose sign bit is clear.

Encoding Examples

The following table illustrates how common values are serialized:

IntegerCScriptNum BytesExplanation
0[] (empty)Zero is the empty byte array
1[0x01]Magnitude fits in 7 bits, sign bit clear
-1[0x81]Magnitude 0x01 with sign bit 0x80
127[0x7f]Largest value in a single byte (bit 7 free for sign)
-127[0xff]0x7f magnitude with sign bit: 0x7f | 0x80 = 0xff
128[0x80, 0x00]Needs extra byte: 0x80 alone would be negative zero
-128[0x80, 0x80]Magnitude 0x80 with sign bit set in second byte
255[0xff, 0x00]Magnitude fills first byte; sign byte appended
-255[0xff, 0x80]Same magnitude; sign bit set in second byte

Zero and Negative Zero

Zero is represented as an empty byte array (zero-length stack element). The encoding [0x80] technically represents negative zero, but Bitcoin Script treats it as false in boolean contexts, the same as the empty array. The minimal encoding rule (described below) rejects [0x80] as a non-canonical numeric input.

The Minimal Encoding Rule

To prevent transaction malleability, Bitcoin consensus requires that numeric stack values use the fewest bytes possible. This is known as the minimal encoding rule. It rejects any encoding where the most significant byte, excluding the sign bit, is zero, with one exception: when the second-to-last byte has its high bit set, an extra zero byte is necessary to avoid colliding with the sign bit.

In code, the check looks like this:

// Reject non-minimal encodings
// If the MSB of the last byte (excluding sign) is zero,
// the number could be encoded in fewer bytes.
// Exception: the previous byte's high bit is set,
// so the zero byte carries only the sign.

if ((last_byte & 0x7f) == 0) {
    if (data.size() <= 1 || (data[data.size() - 2] & 0x80) == 0) {
        // Non-minimal encoding: reject
        return error;
    }
}

For example, encoding the number 1 as [0x01, 0x00] would be rejected because the trailing zero byte is unnecessary. But [0xff, 0x00] for 255 is valid because the first byte's high bit (0x80) is set, so the second byte is needed to hold the (clear) sign bit.

Size Limits

CScriptNum enforces a configurable maximum byte length when deserializing stack elements. This limit determines the range of integers that numeric opcodes can process.

The Default 4-Byte Limit

Most arithmetic and comparison opcodes (OP_ADD, OP_SUB, OP_LESSTHAN, OP_WITHIN, and others) enforce the default 4-byte limit. This restricts numeric operands to the range -2,147,483,647 to 2,147,483,647 (roughly ±2.1 billion). Note that this is not the full 32-bit signed range: the sign-magnitude encoding means both +0 and -0 exist, so the range is symmetric rather than the asymmetric range of two's complement.

Arithmetic results can temporarily exceed 4 bytes on the stack, but they become invalid if passed to another numeric opcode that enforces the limit.

The 5-Byte Exception for Timelocks

OP_CHECKLOCKTIMEVERIFY (CLTV), introduced in BIP 65, compares a stack value against the transaction's nLockTime field. Because nLockTime is a 32-bit unsigned integer, a 4-byte signed CScriptNum can only represent values up to 2,147,483,647, which in Unix timestamp terms corresponds to January 2038. To avoid this year 2038 problem, CLTV instructs CScriptNum to accept 5-byte values, extending the representable range to cover all possible nLockTime values up to 4,294,967,295 (the year 2106).

OP_CHECKSEQUENCEVERIFY (CSV) does not need this extension because its sequence values fit within the signed 4-byte range.

Proposals for 64-Bit Arithmetic

The default 4-byte limit is a long-standing constraint on Bitcoin Script's programmability. Representing satoshi amounts requires up to 51 bits of precision (the maximum supply of 21 million BTC is 2.1 quadrillion satoshis), which exceeds the 4-byte limit. Several proposals have explored widening CScriptNum to support 8-byte (64-bit) values, which would enable opcodes to natively handle transaction amounts and unlock more expressive covenant designs. As of late 2026, these proposals remain under discussion and have not been activated on mainnet.

Why It Matters

The CScriptNum encoding affects every aspect of Bitcoin Script that involves numbers: from simple arithmetic in multisig thresholds to the timelock comparisons that secure payment channels. Developers working with raw Script, whether writing Miniscript policies, Tapscript spending conditions, or discreet log contracts, must serialize numbers correctly or risk creating invalid transactions.

The minimal encoding rule is particularly important for consensus. Before this rule was universally enforced, the same number could be represented multiple ways, which created a vector for transaction malleability: a third party could change the encoding without invalidating the signature, altering the transaction ID. The enforcement of minimal encoding, alongside SegWit's witness data separation, closed this class of malleability bugs.

For layer-2 protocols like the Lightning Network and Spark, correct number encoding is critical in the on-chain transactions that enforce protocol rules: timelocked refund paths, sequence-locked revocation checks, and threshold conditions all rely on CScriptNum values being interpreted identically by every node in the network.

Use Cases

Timelock Conditions

OP_CHECKLOCKTIMEVERIFY and OP_CHECKSEQUENCEVERIFY read CScriptNum values from the stack to enforce time-based or block-height-based spending restrictions. These are the foundation of HTLCs, timelocked refund paths, and payment channel dispute mechanisms.

Arithmetic in Spending Conditions

Opcodes like OP_ADD, OP_SUB, OP_LESSTHAN, and OP_WITHIN use CScriptNum for on-stack arithmetic. While Bitcoin Script's arithmetic capabilities are limited compared to platforms like Ethereum, they enable threshold checks, counter-based logic, and numeric validation within redeem scripts and Tapscript leaves.

OP_RETURN Data Encoding

Protocols that embed structured data in OP_RETURN outputs sometimes use CScriptNum encoding for numeric fields, though this is a convention rather than a consensus requirement since OP_RETURN data is not executed.

Script Debugging and Testing

Developers building and testing Bitcoin Script programs with tools like script debuggers need to understand CScriptNum to correctly interpret stack contents. A stack element like [0x80, 0x80] might look like a two-byte value, but it actually represents -128.

Risks and Considerations

Encoding Errors

Manually constructing CScriptNum values is error-prone. Common mistakes include using big-endian byte order, forgetting the sign byte when a magnitude fills bit 7, or producing non-minimal encodings. Any of these will cause the script interpreter to reject the transaction. Developers should rely on well-tested libraries rather than hand-encoding numbers.

Overflow Behavior

Arithmetic opcodes can produce results larger than 4 bytes, and those results remain valid on the stack. However, passing an oversized result to another arithmetic opcode triggers a script failure. This can create subtle bugs: a script might work for small inputs but fail when intermediate sums cross the 4-byte boundary. The Bitcoin Script programmability research article discusses these limitations in depth.

Sign-Magnitude Quirks

The existence of both positive zero (empty array) and negative zero ([0x80]) means that naive equality checks between CScriptNum values and raw stack bytes can produce unexpected results. While the minimal encoding rule prevents negative zero from appearing in validated scripts, developers writing consensus-compatible code must handle this edge case.

4-Byte Range Limitation

The 4-byte default cap means Script cannot natively represent Bitcoin amounts (which require up to 51 bits). This limitation affects covenant proposals that need to introspect transaction values, and it is one of the motivations behind the ongoing 64-bit arithmetic discussions in the Bitcoin development community.

This glossary entry is for informational purposes only and does not constitute financial or investment advice. Always do your own research before using any protocol or technology.