Why is the data format for block height in coinbase scriptSigs inconsistent for heights 1-16? BIP-34 claims that in coinbase transactions, the scriptSig should contain the block height. It says the format of this is &quot;serialized CScript&quot;, i.e. a byte indicating the length of the number (n), and then n bytes in little-endian format.
This works if you look at e.g. the most recent block as of this writing—block 640037—whose coinbase scriptSig begins with these 4 bytes: 0x0325c409. 0x03 indicates the length, and then we have (0x25=37 * 256^0) + (0xc4=196 * 256^1) + (0x09=9 * 256^2), or 37 + 50176 + 589824 = 640037.
For obvious reasons mainnet and testnet will never need to encode heights 1-16 in their coinbase. But regtest nodes will. When I run a fresh regtest node (v0.19.1) and generate a couple blocks, I get coinbase scriptSigs that look like this:
height 1: 0x510101
height 2: 0x520101
...
height 16: 0x600101

Clearly it's not serialized CScript, at least in the format described in BIP-34. If it were, those first bytes would indicate data lengths of 81, 82, and 96 respectively, clearly ridiculous. The actual encodings of 1, 2, and 16 in serialized CScript are 0x0101, 0x0102, and 0x0110. It looks to me like the format is height+80 followed by 0x0101. And for some reason this only persists for the first 16 blocks, as 17's scriptSig is 0x01110101.
I tried looking through the bitcoin source code, but I don't write much C++ so it's hard to tell what's going on. No documentation—in the source (as far as I can tell) or the PR for BIP-34 or in the BIP itself seems to indicate that heights 1-16 would have a different format from every other height.
Why is this happening?
The actual encodings of the numbers 1-16 are 0x51, 0x52, etc. This is because the numbers 1-16 are special in Bitcoin script and have their own explicit opcodes. OP_1 is 0x51, OP_2 is 0x52, etc. The correct way to write numbers with their own opcodes is to use those opcodes instead of using a pushdata opcode followed by the number.