§ 07

§ 07 · Blocks

Certificate

The ~60 KB proof of work that rides in front of every block

A Bitcoin block proves its own work: anyone can hash its header and see that the result is small enough. A Pearl block can’t, because its work is a huge matrix multiplication on private data. So every Pearl block carries a separate proof, its certificate.

Think of it as a sealed envelope with a label. The label, the public data, says in 164 bytes which puzzle was solved and which lottery ticket won. The envelope holds a zero-knowledge proof, about 60 KB, that the label is true. Nodes read the label, check the proof, and never see the matrices.

This page is about the bytes. How the certificate is made and what the proof proves are on the certificate (mining) and zero-knowledge proofs.

What’s in a certificate?

Block 130,825’s certificate, field by field. The proof is collapsed to its first bytes; it would fill this page many times over.

Block 130,825 · certificate 240 bytes
03000000 ae3355eec8af1f407f0e935739615e375f35bedcd556a75e13a093387651c83f a4000000 00200000 8000 0000 070100000000 0001031f0000 0000 0000 00000000000000000000000000000000000000000000000000000000 8e17f731a0c6f8e09a7a29f19732fc29f65932b63886013274b920b057e6930f c2e4cb51cff3f95861c45248b6369f5c2dbd623895a3146be39134d060837c8d b4be6b12fbad2ac915cbc0f80f94a1b3fbe55786f3a2912ded00000000000000 00000200 00000400 00930100 02e10200 a8e80000 121216010307bbbb23999fb93b02b1fd3157fba6a4398fd35f6a45d8cf5284db
Field Bytes Value
Certificate version certificate.version 4 @0 3 u32 LE. V1 before block 71,935, V2 until 98,999, V3 (salted noise seeds) from 99,000. 0 = no certificate.
Block hash certificate.blockHash 32 @4 3fc851763893a0135ea756d5dcbe355f375e613957930e7f401fafc8ee5533ae SHA256d of the 108-byte header this certificate proves (stored byte-reversed); must equal the header’s hash.
Public data length certificate.publicDataLength 4 @36 164 164 for dense jobs; longer means MoE (invalid since 91,630).
Common dimension k certificate.publicData.k 4 @40 8192 u32 LE: the shared dimension of A (m×k) and B (k×n).
Noise rank r certificate.publicData.rank 2 @44 128 u16 LE: rank of the low-rank noise; must be ≥ 128 since block 96,251.
MMA type certificate.publicData.mmaType 2 @46 0 0 = Int7 × Int7 → Int32.
Rows pattern certificate.publicData.rowsPattern 6 @48 [0,8] (h=2) Periodic row offsets of the hash tile, stored as (factor−1, length−1) × 3.
Columns pattern certificate.publicData.colsPattern 6 @54 64 columns from [0,1,8,9,16,17,…] (w=64) Periodic column offsets of the hash tile.
Experts e certificate.publicData.experts 2 @60 0 Number of MoE experts; 0 = dense (MoE is invalid on mainnet since 91,630).
Top-k certificate.publicData.topK 2 @62 0 0 for dense jobs.
Reserved certificate.publicData.reserved 28 @64 00000000000000000000000000000000000000000000000000000000 Must be zero.
hash_A certificate.publicData.hashA 32 @92 8e17f731a0c6f8e09a7a29f19732fc29f65932b63886013274b920b057e6930f Keyed-BLAKE3 Merkle root of matrix A (key = job key).
hash_B certificate.publicData.hashB 32 @124 c2e4cb51cff3f95861c45248b6369f5c2dbd623895a3146be39134d060837c8d Keyed-BLAKE3 Merkle root of Bᵀ (B column-major).
hash_jackpot certificate.publicData.hashJackpot 32 @156 b4be6b12fbad2ac915cbc0f80f94a1b3fbe55786f3a2912ded00000000000000 The winning ticket: BLAKE3 keyed with seed_A over the tile transcript; read as a little-endian number and compared with the bound.
m (rows of A) certificate.publicData.m 4 @188 131072 u32 LE; bound into the noise seeds since certificate V3.
n (columns of B) certificate.publicData.n 4 @192 262144 u32 LE; bound into the noise seeds since certificate V3.
Tile row offset certificate.publicData.tRows 4 @196 103168 Smallest row index of the winning hash tile.
Tile column offset certificate.publicData.tCols 4 @200 188674 Smallest column index of the winning hash tile.
Proof length certificate.proofLength 4 @204 59560 u32 LE; at most 60,000 bytes.
ZK proof (first 32 of 59,560 bytes) certificate.proof 32 @208 59,560 bytes, collapsed pow_bits(3) ‖ rate_bits(3) ‖ zeta(16) ‖ Plonky2 proof: a STARK wrapped in two Plonky2 recursions. Not covered by the block hash.
ProofCommitment check
SHA256d(version ‖ public data) matches the header ✓
Certificate
V3 · 59,768 bytes
Proof
59,560 bytes (99.7%)
The proof itself is 59,560 bytes; only its first 32 are shown.
A version 2 or 3 certificate
Field Size Type Description
Version 4 uint32 LE 03000000 since block 99,000.
Block hash 32 hash SHA256d of the header this certificate proves, in internal byte order. Must match the header.
Public data length 4 uint32 LE 164 (a4000000) for a dense job. Not present in version 1.
Public data 164 bytes What was mined and which ticket won (table below). The header’s proof commitment is a hash of it.
Proof length 4 uint32 LE 59,560 (a8e80000) on mainnet today. At most 60,000.
Proof 59,560 bytes The zero-knowledge proof: a STARK wrapped in two Plonky2 layers. Not covered by the block hash.
proof · 59,560 bytes (99.65%)block hashpublic data · 164 bytesverlenlen043640204208
A 59,768-byte V3 certificate, to scale. Everything except the proof fits in 208 bytes, about 0.35%; the zoom below shows those bytes to scale.

Which versions exist?

Three real ones, each required in its own range of heights, plus an empty placeholder:

Version Blocks Layout after the 4-byte version Size on mainnet
0 none (placeholder) nothing 4 bytes, 00000000
1 1 to 71,934 block hash (32) ‖ public data (164, fixed) ‖ proof length (4) ‖ proof 59,764 bytes (block 1)
2 71,935 to 98,999 block hash (32) ‖ public data length (4) ‖ public data ‖ proof length (4) ‖ proof about 59.8 KB
3 99,000 onward same bytes as version 2 59,768 bytes
  • Version 2 came with the Mixture-of-Experts hard fork at block 71,935. Its public data could be longer than 164 bytes, up to 4,807, to describe expert routing; hence the length field. From block 91,630 a soft fork rejects anything longer than 164 again, so mainnet certificates are dense.
  • Version 3 came with the salted noise-seed hard fork at block 99,000. Not one byte of the format changed. What changed is how the verifier reads it: the noise seeds now also commit to the matrix sizes, and the proof commitment hashes a 3. See noise seeds.
  • Version 0 is a bare 00000000: “no certificate here”. It stands in for certificates in block templates and in headers replies sent without certificates.

The genesis block has a stand-in version 1 certificate: its block hash, 164 zero bytes of public data and a 4-byte “proof”, deadbeef. That’s 208 bytes, and it’s never checked.

What’s in the public data?

Twelve fields in 164 bytes. Here they are for block 130,825, decoded:

Dense public data, 164 bytes (block 130,825's values)
Field Size Type Description
k 4 u32 Shared dimension of A (m × k) and B (k × n): 8,192.
Noise rank r 2 u16 128. At least 128 since block 96,251.
MMA type 2 u16 0 = 7-bit × 7-bit integers into 32-bit sums.
Rows pattern 6 pattern 070100000000: rows 0 and 8 of the tile, so h = 2.
Columns pattern 6 pattern 0001031f0000: column pairs every 8 columns, 0, 1, 8, 9, … 248, 249, so w = 64.
Experts, top-k 4 u16 × 2 0, 0: a dense job, not Mixture-of-Experts.
Reserved 28 zeros Must be zero.
hash_A 32 hash Keyed-BLAKE3 Merkle root of matrix A: 8e17f731…57e6930f.
hash_B 32 hash The same for B, stored column by column: c2e4cb51…60837c8d.
hash_jackpot 32 hash The winning ticket, b4be6b12…a2912ded followed by seven zero bytes. Read little-endian and compared with the target.
m 4 u32 Rows of A: 131,072.
n 4 u32 Columns of B: 262,144.
Tile row 4 u32 Where the winning tile sits: row 103,168.
Tile column 4 u32 Column 188,674.

The first 52 bytes are the mining configuration; the job key hashes them along with the header. Together the fields say: “A 131,072 × 8,192 matrix times an 8,192 × 262,144 matrix, with rank-128 noise, cut into 2 × 64 tiles; the tile starting at row 103,168, column 188,674 produced this ticket.” That’s a multiplication of about 2.8 × 10¹⁴ multiply-accumulates, of which the winning tile is 1,048,576.

How a 6-byte pattern becomes a list of offsets

Each pattern is three (stride, count) pairs, stored as (factor − 1, count − 1). The first stride is just its factor; each later stride is its factor times the span of everything before it.

  • Rows 07 01 00 00 00 00: stride (7 + 1) × 1 = 8, count 1 + 1 = 2, so offsets 0, 8. The other pairs add nothing.
  • Columns 00 01 03 1f 00 00: stride 1, count 2 gives 0, 1 (span 2); then stride (3 + 1) × 2 = 8, count 31 + 1 = 32, repeats that pair every 8 columns: 0, 1, 8, 9, …, 248, 249, 64 offsets.

That’s exactly the reference GPU miner’s pattern, which matches the sums one GPU thread holds in its registers. Block 130,827’s certificate decodes to a different shape, 8 × 16, from rows 07 01 01 03 00 00 (0, 8, 32, 40, 64, 72, 96, 104) and columns 00 01 0f 07 00 00 (0, 1, 32, 33, …, 224, 225). Both are 128 cells. Miners choose. See GPUs and tiles.

What’s in the proof bytes?

A short preamble, then the proof itself:

Size Field Block 130,825
3 proof-of-work bits for the three proof layers 12 12 16 = 18, 18, 22
3 rate bits for the three layers 01 03 07 = 1, 3, 7
16 zeta, a challenge point (two 64-bit field elements) bbbb2399…
59,538 the Plonky2 proof …

These are settings of the proof system, not of the mining. Block 1’s proof starts 12 12 16 02 03 07: the same, except a rate of 2 for the first layer. (Comments in Pearl’s code describe a 32-byte “preprocessed digest” in the preamble; the actual format carries the 16-byte zeta instead.)

How big can it be, and does it count?

Certificate today
59,768 B
4 + 32 + 4 + 164 + 4 + 59,560
Proof today
59,560 B
same at k = 2,048 and 8,192
Public data
164 B
dense; MoE was longer
Proof cap
60,000 B
MaxZKProofSize
Certificate cap
65,000 B
CertificateMaxSize
Counted in vsize
0 vB
excluded by consensus

The proof barely changes size with the size of the multiplication. In the research for this site, proofs for k = 2,048 and k = 8,192 were both exactly 59,560 bytes.

The block size limit, 1,000,000 vbytes, counts the header and transactions (witness bytes at a quarter) and not the certificate. Pearl’s code says why in one line: the certificate is “proof metadata, not block data”. The certificate sits on top of the 4,000,000-byte cap for header and transactions, so the largest possible block message is 4,065,000 bytes.

That’s why sizes disagree. Block 130,827 is 64,379 bytes on disk and on explorers like Blockbook, but only 2,795 vbytes to consensus. A block holding nothing but its coinbase shows up as roughly 60 KB.

Make a guess

Could a miner pad its certificate with extra bytes to slow down other nodes?

Think of your answer first, then check it.

Answer Only up to 65,000 bytes, and the padding must still be a valid proof.

The decoder refuses any certificate over 65,000 bytes or any proof over 60,000, before trying to verify it. And the bytes are a proof: a node runs the verifier over them, so garbage fails. The caps leave a little headroom over today’s 59,768 bytes.

Where does the certificate travel?

Everywhere the header goes:

  • In blocks, first: certificate, then header, then transactions. See blocks.
  • In header sync. A headers message carries up to 100 entries, each a certificate (or 00000000) followed by a 108-byte header. Nodes verify every certificate while syncing headers.
  • To light clients, which also verify each header’s certificate. So a Pearl light client downloads about 60 KB per block header, where a Bitcoin light client needs 80 bytes.
Code: the wire format

The layouts are documented at the top of certificate.go (node/wire/certificate.go:18-35 at v1.5.2, on GitHub), and a V2/V3 certificate is written in certificate_v2.go (node/wire/certificate_v2.go:80-98 at v1.5.2, on GitHub) (the version itself is written first by the MsgCertificate wrapper):

node/wire/certificate_v2.go Go
func (c *CertificateV2) Serialize(w io.Writer) error {
	w.Write(c.Hash[:])                                             // 32
	binary.Write(w, binary.LittleEndian, c.PublicDataLen)          // 4
	w.Write(c.PublicDataBytes())                                   // 164
	binary.Write(w, binary.LittleEndian, uint32(len(c.ProofData))) // 4
	w.Write(c.ProofData)                                           // 59,560
	return nil
}

The public data fields are listed in proof.rs (zk-pow/src/api/proof.rs:76-105 at v1.5.2, on GitHub), and the vsize rule that leaves the certificate out is in vsize.go (node/blockchain/vsize.go:39-52 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.