§ 01 · Bytes & numbers
Byte order
Why Pearl's numbers are stored backwards, and its hashes shown backwards
On this page 5 sections
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-endianLittle-endian: Storing a number that spans several bytes with its smallest byte first. Pearl, like Bitcoin, stores numbers this way, and shows hashes with their bytes reversed.. The other way, biggest byte first, is big-endianBig-endian: Storing a number that spans several bytes with its biggest byte first, the way people write numbers. Pearl uses it for private keys and inside SHA-256, but stores most integers little-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.
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:
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:
| 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. |
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.
| 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. |
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:
| 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. |
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 ticketTicket: A 256-bit BLAKE3 hash of a tile's transcript. It wins a block if, read as a number, it is at most the target times the work that tile cost. 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:
| Field | Bytes | Value |
|---|---|---|
| low bytes | 25 @0 | least significant bytes come first |
| high bytes | 7 @25 | seven zero bytes, the most significant, stored last |
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
0x176ad0ebor 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:
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).