Saturday, December 28, 2024

bitcoin core – Understanding Script’s quantity encoding, CScriptNum

Numbers in Script are represented as byte vectors of as much as 4 bytes and interpreted as signed integers between -2^31+1 and a pair of^31-1. That is aside from OP_0, OP_1NEGATE and OP_1 via OP_16 which permit to characterize small numbers (from -1 to 16) through the use of opcodes immediately.

These 31 bits numbers are encoded in little-endian with sign-magnitude. Notice the byte vector measurement isn’t mounted: there isn’t any padding for small integers. Here’s a few examples.

Decimal worth 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 might be discovered right here. The Bitcoin Core check framework has a Python reimplementation of the serialization. Here’s a simplified and extremely commented model:

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. Executed manually to keep away from padding.
    abs_n = abs(n)
    whereas abs_n > 0:
        res.append(abs_n & 0xff)
        abs_n >>= 8

    # If n is adverse and the serialized quantity doesn't have its most important
    # bit set, we will simply set it by including 0x80 to its most important byte. This
    # avoids requiring a further byte simply to set the signal bit.
    msb_set = res[len(res) - 1] >= 0x80
    if not msb_set and n < 0:
        res[len(res) -1] += 0x80

    # Nevertheless it does imply that if the serialized quantity has its most important
    # bit set we have to push a further byte so 1) it isn't handled as adverse
    # if it is constructive 2) it isn't substracted from its precise worth.
    if msb_set:
        # If n is constructive add a byte so the signal bit is not set. If it is adverse add
        # one other byte for the signal bit so it isn't substracted from the quantity itself.
        if n >= 0:
            res.append(0x00)
        else:
            res.append(0x80)

    return bytes(res)

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles