§ 01

§ 01 · Bytes & numbers

Byte order

Why Pearl's numbers are stored backwards, and its hashes shown backwards

When a number takes several bytes, someone has to decide which byte goes first. That choice is called byte order, or endianness.

Pearl, like Bitcoin, stores most numbers smallest byte first. That’s little-endian. The other way, biggest byte first, is big-endian, and it’s how people write numbers on paper.

This one choice explains most of the “why does this look backwards?” moments you’ll have reading raw Pearl data.

Try it

Pick a byte width, type a number and compare the two orders. The converter shows the bytes as written and as Pearl stores them.

Hex converter

Decimal, hex and binary, and the bytes in both orders

Big-endian as written
0001ff0b
Little-endian as stored
0bff0100
Runs in your browser · nothing is sent src/lib/bytes.ts

What does little-endian look like?

Think of a date. Writing 2026-10-20 puts the biggest unit first. Writing 20-10-2026 puts the smallest first. Same date, different order.

Take the number 1,792,512,899. In hex it’s 0x6ad79383, four bytes: 6a d7 93 83. Little-endian stores them the other way round:

big-endian (as written)little-endian (as stored)6ad793838393d76a
The same 4-byte number in both orders. Only the order of the bytes changes; the characters inside each byte stay together.

That number isn’t random. It’s the time of block 130,825, and 83 93 d7 6a is exactly what sits in its header:

Block 130,825 · header 108 bytes
00000020 d3221e43dc59c5d0dae1ee53987d6a4abeef42567d682017a6edd6e0de7ac7ab 2a051d25484d57e49a1f3b757685403ed06f558bb06a1804c34fac74ee7f5fab 8393d76a ebd06a17 ce6de312e0e4ca6c862c05835314db00cf02c13a20ea4810940f09b64ed8ba86
Field Bytes Value
Version header.version 4 @0 0x20000000 int32 LE. BIP-9 style 0x20000000 since block 1; no consensus meaning in Pearl.
Previous block header.prevBlock 32 @4 abc77adee0d6eda61720687d5642efbe4a6a7d9853eee1dad0c559dc431e22d3 Hash of the parent block, stored byte-reversed; shown in display order.
Merkle root header.merkleRoot 32 @36 ab5f7fee74ac4fc304186ab08b556fd03e408576753b1f9ae4574d48251d052a Root of the SHA256d Merkle tree of this block’s txids, stored byte-reversed.
Time header.timestamp 4 @68 1792512899 (2026-10-20 16:14:59 UTC) uint32 LE Unix seconds. Must be at least 1 s after the parent and at most 5 min in the future.
Bits header.bits 4 @72 0x176ad0eb Compact target. Difficulty 40208379.91968057. The certificate's jackpot hash (not this header's hash) must meet it.
Proof commitment header.proofCommitment 32 @76 86bad84eb6090f941048ea203ac102cf00db145383052c866ccae4e012e36dce SHA256d(LE32(certificate version) ‖ public data). Replaces Bitcoin’s nonce: Pearl headers have no nonce.
Block hash
3fc851763893a0135ea756d5dcbe355f375e613957930e7f401fafc8ee5533ae
Height
130,825
Time
2026-10-20 16:14:59 UTC
Difficulty
40,208,379.92

To read any little-endian field, reverse the bytes, then read the hex normally. Reverse whole bytes, not single characters: 83 stays 83.

The other little-endian fields in the same header
Field Stored bytes Reversed Value
version 00 00 00 20 20 00 00 00 0x20000000 = 536,870,912
time 83 93 d7 6a 6a d7 93 83 1,792,512,899 (2026-10-20 16:14:59 UTC)
bits eb d0 6a 17 17 6a d0 eb 0x176ad0eb, the compact target

Amounts in transactions work the same way, just with 8 bytes.

Why are hashes shown backwards?

Here’s the part that trips everyone up. A hash isn’t a number you do arithmetic with, so you’d expect it to be shown exactly as stored. It isn’t.

Block 130,825’s header points to its parent with the bytes d3221e43…de7ac7ab. But explorers show the parent’s hash as abc77adee0d6eda61720687d5642efbe4a6a7d9853eee1dad0c559dc431e22d3. It’s the same 32 bytes, reversed.

Block 130,825 · header 108 bytes
00000020 d3221e43dc59c5d0dae1ee53987d6a4abeef42567d682017a6edd6e0de7ac7ab 2a051d25484d57e49a1f3b757685403ed06f558bb06a1804c34fac74ee7f5fab 8393d76a ebd06a17 ce6de312e0e4ca6c862c05835314db00cf02c13a20ea4810940f09b64ed8ba86
Field Bytes Value
Version header.version 4 @0 0x20000000 int32 LE. BIP-9 style 0x20000000 since block 1; no consensus meaning in Pearl.
Previous block header.prevBlock 32 @4 abc77adee0d6eda61720687d5642efbe4a6a7d9853eee1dad0c559dc431e22d3 Hash of the parent block, stored byte-reversed; shown in display order.
Merkle root header.merkleRoot 32 @36 ab5f7fee74ac4fc304186ab08b556fd03e408576753b1f9ae4574d48251d052a Root of the SHA256d Merkle tree of this block’s txids, stored byte-reversed.
Time header.timestamp 4 @68 1792512899 (2026-10-20 16:14:59 UTC) uint32 LE Unix seconds. Must be at least 1 s after the parent and at most 5 min in the future.
Bits header.bits 4 @72 0x176ad0eb Compact target. Difficulty 40208379.91968057. The certificate's jackpot hash (not this header's hash) must meet it.
Proof commitment header.proofCommitment 32 @76 86bad84eb6090f941048ea203ac102cf00db145383052c866ccae4e012e36dce SHA256d(LE32(certificate version) ‖ public data). Replaces Bitcoin’s nonce: Pearl headers have no nonce.
Block hash
3fc851763893a0135ea756d5dcbe355f375e613957930e7f401fafc8ee5533ae
Height
130,825
Time
2026-10-20 16:14:59 UTC
Difficulty
40,208,379.92

The same happens to the block’s own hash. SHA256d of the header comes out as ae3355ee…7651c83f. Reversed, that’s the hash you see everywhere: 3fc851763893a0135ea756d5dcbe355f375e613957930e7f401fafc8ee5533ae.

Why? In Bitcoin’s original code, a block hash was treated as a 256-bit little-endian number so it could be compared with the target. When the code printed that number, it printed it the human way, biggest byte first, which reversed the bytes. The habit stuck, and Pearl inherited it unchanged.

Transactions do the same. An input names the transaction it spends by its txid, stored internally. The payment below spends 33218153…70553959, but its first input holds 5939557091bae792…e253812133 on the wire:

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

Where does byte order decide who wins a block?

In Bitcoin, the reversed hash display is a leftover: the block hash read as a little-endian number is what gets compared with the target.

Pearl never compares the block hash with the target. Instead, the winning check reads a different hash, the BLAKE3 mining ticket called hash_jackpot, as a little-endian 256-bit number. A small number has zero high bytes, and in little-endian the high bytes come last. So a winning ticket’s zeros sit at the end of its raw bytes.

Block 130,825’s winning ticket, as stored in its certificate:

Toy example 32 bytes
b4be6b12fbad2ac915cbc0f80f94a1b3fbe55786f3a2912ded 00000000000000
Field Bytes Value
low bytes 25 @0 least significant bytes come first
high bytes 7 @25 seven zero bytes, the most significant, stored last
Block 130,825's ticket (hash_jackpot), raw bytes. Read little-endian, it becomes 0x00000000000000ed2d91…, small enough to win.

Reversed, the ticket is 0x00000000000000ed2d91…bbeb4. The bound it had to beat was 0x00000000000006ad0eb0… (the target times the 1,048,576 multiply-accumulates its tile cost), and it did. Read the bytes the wrong way round and the same ticket would be an enormous number that loses. The tickets and target page explains the bound.

Is anything big-endian?

Yes, a few things:

  • Private keys are 32-byte big-endian numbers. Key 1 is 00…0001.
  • Inside SHA-256, the algorithm reads its input in 4-byte big-endian words. You never see this from outside.
  • Hex written by people, like 0x176ad0eb or a hash in display order, reads biggest first.

The rule of thumb for Pearl’s own fields: if it’s a count, an amount, a time or a version, it’s stored little-endian. If it’s a hash, it’s stored in internal order and shown reversed.

Code: reversing bytes

In JavaScript:

JavaScript
const reverseHex = (hex) => hex.match(/../g).reverse().join('');
reverseHex('8393d76a');                    // '6ad79383'
parseInt(reverseHex('8393d76a'), 16);      // 1792512899

pearld prints a hash by swapping its bytes end to end before hex-encoding, in Hash.String() (node/chaincfg/chainhash/hash.go:73-80 at v1.5.2, on GitHub). Integers are written with Go’s binary.LittleEndian in writeBlockHeader (node/wire/blockheader.go:184-215 at v1.5.2, on GitHub).

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.