§ 01

§ 01 · Bytes & numbers

Bytes

Eight bits, one number from 0 to 255

A byte is a group of eight bits. It can hold one whole number from 0 to 255.

That’s the basic unit of everything in Pearl. A private key is 32 bytes. A signature is 64. A block header is 108. Even the matrices miners multiply are stored as long rows of bytes.

What can one byte hold?

Each of the eight bits is a 0 or a 1, and each is worth twice the one to its right. Add up the bits that are on and you get the byte’s value.

128643216842111000010= 194 = 0xc2
The byte 0xc2. The bits worth 128, 64 and 2 are on: 128 + 64 + 2 = 194.

All eight bits off is 0. All eight on is 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1 = 255. That gives 256 possible values, which is exactly two hex characters’ worth.

How do bytes make bigger numbers?

By working in teams. Two bytes side by side act like a two-digit number in base 256.

Bytes Largest value Where Pearl uses it
1 255 short lengths and counts
2 65,535 the noise rank in a certificate
4 4,294,967,295 version, timestamp, bits, output index, locktime
8 about 1.8 × 10¹⁹ amounts in grains
32 about 1.2 × 10⁷⁷ hashes, private keys, x-only public keys

Which byte of the team comes first is a real question with a surprising answer. It has its own page: byte order.

How big are Pearl’s pieces?

Most things in Pearl have a fixed size, which is what makes raw data readable at all. If you know a hash is 32 bytes, you know exactly where the next field starts.

Block header
108 bytes
Bitcoin: 80
Hash
32 bytes
block hash, txid, Merkle root
Public key
32 bytes
x-only, as in Taproot
Schnorr signature
64 bytes
65 with a sighash byte
Amount
8 bytes
a whole number of grains
Certificate
59,768 bytes
in front of every block today
Public data
164 bytes
what the proof is about
XMSS signature
2,340 bytes
optional, post-quantum
Matrix entry
1 byte
a whole number in [−64, 64]

Things that vary in length, like a list of inputs or a script, get a small length prefix in front. That prefix is a CompactSize.

How big is a kilobyte here?

This site uses KB for 1,000 bytes: the certificate in front of every block is about 59.8 KB. A few places use powers of two instead, written KiB (1,024 bytes) and GiB (2³⁰ bytes):

  • BLAKE3 hashes data in 1 KiB chunks, and Pearl pads mining matrices to a multiple of 1,024 bytes before fingerprinting them.
  • A matrix of 131,072 × 8,192 one-byte entries, the size of matrix A in block 130,825’s certificate, is exactly 1 GiB.

Block size is counted differently again, in vbytes: at most 1,000,000 per block, with the certificate not counted at all.

How are negative numbers stored?

A byte only holds 0 to 255, but sometimes you need negatives. The standard trick is two’s complement: the top half of the range stands for negative numbers.

Value −128 −64 −1 0 1 63 64 127
Byte 80 c0 ff 00 01 3f 40 7f

The rule: to store −x, take 256 − x. So −1 becomes 255 (ff) and −64 becomes 192 (c0). The clever part is that ordinary addition still works. Add 1 to ff and it wraps around to 00, just as −1 + 1 = 0.

Pearl’s mining uses this directly. Every entry of a miner’s matrices is a small whole number between −64 and 64, stored as one signed byte. Here is the first row of the toy matrix A from Pearl’s toy mining example, and the 16 bytes that represent it:

Toy example 16 bytes
fd010404 fdff0402 040303ff fcfdfd00
Field Bytes Value
−3, 1, 4, 4 4 @0 −3 is 0xfd (256 − 3 = 253)
−3, −1, 4, 2 4 @4 −1 is 0xff
4, 3, 3, −1 4 @8 040303ff
−4, −3, −3, 0 4 @12 −4 is 0xfc
A toy matrix row, 16 signed entries, one byte each (row-major). Toy numbers from the research worked example.

The integers page explains why Pearl picks the range −64 to 64, and why the sums use 4-byte numbers.

How is text stored?

With a lookup table. ASCII gives every common character a number below 128, so each character fits in one byte.

Pearl’s mainnet network magic is the constant 0x50524C4D. Written out, its bytes are 50 52 4c 4d, and in ASCII that’s “P”, “R”, “L”, “M”. (Stored smallest byte first, it appears as 4d4c5250 in Pearl’s block files. Pearl’s peer-to-peer traffic is always encrypted, so it never shows up in the clear on the network.)

Text also appears inside real blocks. The coinbase of block 130,825 carries the bytes 2f706f6f6c2e6b7279707465782e636f6d2f, which read as “/pool.kryptex.com/”, the name of the pool that mined it.

What does a real transaction look like as bytes?

Here is a real Pearl payment, every byte colored by its field. Hover to name each one. It is one input, two outputs and one 64-byte signature.

A typical payment: 1 input, 2 outputs 205 bytes
01000000 00 01 01 5939557091bae792be66f26cbd83fc78f16a690a854109e8a1d115e253812133 ab020000 00 ffffffff 02 b0359f1100000000 22 5120d7ab02c96a342bdddb5d07b9f3ebd6c04e077ff41418110b9a1e5997439416b2 00c2eb0b00000000 22 512062e9da3682aab288392cf7218bafc36287288abb3ce200c5d1a50aa234073b2b 01 40 4686c7014749649b25555874c7772ecb7a44e85454893ed5888db11ebcdc82a22d74ce1a1671d02810f5ba1dca3277409e95a653ba9644099f7da26be7a86ba2 00000000
Field Bytes Value
Version version 4 @0 1 int32 little-endian. Pearl wallets and pearld coinbases use version 1; relay policy accepts 1-2.
Marker marker 1 @4 0x00 Segwit marker (BIP-144): says witness data follows the outputs.
Flag flag 1 @5 0x01 Witness flag; 0x01 is the only value Pearl accepts.
Input count vin.length 1 @6 1 CompactSize number of inputs.
Previous txid vin[0].txid 32 @7 33218153e215d1a1e80941850a696af178fc83bd6cf266be92e7ba9170553959 The transaction being spent, stored byte-reversed (internal order); shown here in normal display order.
Output index vin[0].vout 4 @39 683 Which output of that transaction is spent (uint32 LE).
scriptSig length vin[0].scriptSig.length 1 @43 0 Always 0 for non-coinbase inputs in Pearl: every spend uses the witness instead.
Sequence vin[0].sequence 4 @44 0xffffffff Final (0xffffffff): no RBF signal, no relative locktime, and locktime is ignored for this input. Pearl wallets use this.
Output count vout.length 1 @48 2 CompactSize number of outputs.
Amount vout[0].value 8 @49 295,646,640 grains (2.95646640 PRL) int64 little-endian, in grains (1 PRL = 100,000,000 grains).
Script length vout[0].scriptPubKey.length 1 @57 34 CompactSize length of the locking script.
Locking script vout[0].scriptPubKey 34 @58 prl1p674s9jt2xs4amk6aq7ul867kcp8qwll5zsvpzzu6revewsu5z6eqv9d5z4 OP_1 OP_PUSHBYTES_32 <output key Q>: a Taproot output.
Amount vout[1].value 8 @92 200,000,000 grains (2.00000000 PRL) int64 little-endian, in grains (1 PRL = 100,000,000 grains).
Script length vout[1].scriptPubKey.length 1 @100 34 CompactSize length of the locking script.
Locking script vout[1].scriptPubKey 34 @101 prl1pvt5a5d5z42egswfv7uscht7rv2rj3z4m8n3qp3w35592ydq88v4seuhn70 OP_1 OP_PUSHBYTES_32 <output key Q>: a Taproot output.
Witness items witness[0].length 1 @135 1 Number of witness stack items for input 0.
Schnorr signature length witness[0][0].length 1 @136 64 CompactSize length of this witness item.
Schnorr signature witness[0][0] 64 @137 4686c7014749649b25555874c7772ecb7a44e85454893ed5888db11ebcdc82a22d74ce1a1671d02810f5ba1dca3277409e95a653ba9644099f7da26be7a86ba2 64-byte BIP-340 signature (R.x ‖ s) with SIGHASH_DEFAULT: a Taproot key-path spend.
Locktime locktime 4 @201 0 0: no time lock.
txid
d919e02a4adfd739ceef31d98803f0ddd24e1b1fa67b0fe572cdec42c1b384c9
Size
205 bytes · 154 vbytes
Block
130,825
Fee
7,915 grains

Read it from left to right and you can see the sizes from the table above: 4 bytes of version, a 32-byte previous txid, 8-byte amounts, a 64-byte signature. The small one-byte fields between them (01, 02, 22, 40) are counts and lengths.

Code: bytes in JavaScript

Browsers handle raw bytes with Uint8Array. Signed bytes use Int8Array, which applies two’s complement for you:

JavaScript
const signed = Int8Array.of(-3, 1, 4, 4);
new Uint8Array(signed.buffer);              // Uint8Array [253, 1, 4, 4], i.e. fd 01 04 04
new TextEncoder().encode('PRLM');            // Uint8Array [80, 82, 76, 77] = 50 52 4c 4d

The codex

Suggested

Up and down arrows move through the results, Enter opens one, Escape closes. Anywhere on the site, slash or Command K opens this search, and the square brackets [ and ] go to the previous and next page.