1

What is a simple explanation of CScriptNum, the number encoding used by arithmetic opcodes in Bitcoin Script?

What would an illustrative pseudocode/python example look like?

What should the encoding of the following integers be (in hexadecimal)?

  • 42
  • 88
  • 257
  • 17000
  • 8589934592

When is it not 4 bytes?


See Also:

Why are arithmetic script opcodes limited to 4 byte operands?

Arithmetic operations in Bitcoin (OP_ADD, etc)

Compact integer in Scripts

1 Answer 1

3

Numbers in Script are represented as byte vectors of up to 4 bytes and interpreted as signed integers between -2^31+1 and 2^31-1. This is with the exception of OP_0, OP_1NEGATE and OP_1 through OP_16 which allow to represent small numbers (from -1 to 16) by using opcodes directly.

These 31 bits numbers are encoded in little-endian with sign-magnitude. Note the byte vector size is not fixed: there is no padding for small integers. Here is a few examples.

Integer Script byte vector
-424242 [0x32; 0x79; 0x86]
-0x80 [0x80; 0x80]
-1 [0x81]
0 []
12 [0x0c]
0x82 [0x82; 0x00]
2^31-1 [0xff; 0xff; 0xff; 0x7f]

The reference implementation of the serialization can be found here. The Bitcoin Core test framework has a Python reimplementation of the serialization. Here is a simplified and highly commented version:

def ser_script_num(n):
    res = bytearray()

    # If n is 0, return the empty vector.
    if n == 0:
        return bytes(res)

    # Encode n as little-endian. Done manually to avoid padding.
    abs_n = abs(n)
    while abs_n > 0:
        res.append(abs_n & 0xff)
        abs_n >>= 8

    # If n is negative and the serialized number does not have its most significant
    # bit set, we can just set it by adding 0x80 to its most significant byte. This
    # avoids requiring an additional byte just to set the sign bit.
    msb_set = res[len(res) - 1] >= 0x80
    if not msb_set and n < 0:
        res[len(res) -1] += 0x80

    # However it does mean that if the serialized number has its most significant
    # bit set we need to push an additional byte so 1) it's not treated as negative
    # if it's positive 2) it's not substracted from its actual value.
    if msb_set:
        # If n is positive add a byte so the sign bit isn't set. If it's negative add
        # another byte for the sign bit so it's not substracted from the number itself.
        if n >= 0:
            res.append(0x00)
        else:
            res.append(0x80)

    return bytes(res)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.