Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

How blake3 hashing works

Chapters across this book keep mentioning blake3 hashes: an alias name lives on-chain only as one, a frombox seeds on one, a reply names the bountied message it claims with one, a DNSSEC proof buffer is keyed by one, and the postoffice commitment set is a Merkle tree built entirely out of them. This page goes into what a cryptographic hash actually does, why SithBit hashes things at all, and why blake3 in particular is a good fit.

What a cryptographic hash is

A cryptographic hash function takes an input of any length — a name, an email address, a whole file — and produces a fixed-size output called a digest (32 bytes, for blake3). Think of it as a fingerprint for data. A good one has four properties:

  • Deterministic. The same input produces the same digest, forever, on every machine. This is what makes a digest usable as an identity.
  • Fixed-size. Whether the input is 8 bytes or 8 gigabytes, the digest is exactly 32 bytes.
  • One-way. Computing the digest from the input is one cheap pass. Recovering the input from the digest is not a matter of running anything backwards — no such algorithm exists. The only attack is guessing candidate inputs and hashing each one to check.
  • Collision-resistant. Nobody can find two different inputs that produce the same digest, so a digest can stand in for its input without fear of an impostor.

A consequence of these properties is the avalanche effect: changing a single character of the input reshuffles essentially every bit of the digest. The two digests give no hint that the inputs were nearly identical.

Three inputs of very different lengths each pass through blake3 and each produce a digest of exactly 32 bytes. Two of the inputs differ by only one letter, yet their digests share nothing — the avalanche effect. marshall marshalL arbitrarily long input — a whole file works too blake3 8f9b098fe8…e81b1aa310 14d9e93e9c…6d64ccbf36 10debc9c08…4b5b280d68 one letter changed — nothing in common always exactly 32 bytes

(The digests above are real blake3 outputs, shown truncated; each is 32 bytes — 64 hex digits — in full.)

One-wayness deserves its own picture, because it is the property the rest of this page leans on: publishing a digest does not publish the input.

Hashing an input into a digest is one cheap forward pass. The reverse direction is crossed out: no algorithm recovers the input from the digest — an attacker can only guess candidate inputs and hash each one to check. [email protected] e72e1cd7…21912055 hashing: one cheap pass nothing computes the input back — only guess-and-check

Why SithBit hashes things at all

Three recurring jobs in the protocol are hash-shaped:

  1. Turning names into account addresses. Solana derives program-owned accounts (PDAs) from seeds, and each seed is capped at 32 bytes. An alias or domain name is a variable-length string that may well exceed that — but its blake3 digest is always exactly 32 bytes, so the digest is the seed. Determinism does the rest: every client, and the program itself, hashes the lowercased name and lands on the same account. See the Program & PDA reference.

  2. Keeping address strings off the chain. No SithBit instruction or account carries an address string. A message account stores only the blake3 hash of the normalized from address, and the frombox seeds on the same hash — the readable headers travel inside the sealed body. One-wayness is what makes this worth doing, with an honest caveat: a hash of a guessable input can be confirmed by hashing the guess, so this hides the address book rather than encrypting it — see the threat model.

  3. Naming something without repeating it — a checkable commitment. A bountied reply stores reply_to_hash, the blake3 of the original message account’s base58 address. At claim time the program hashes the account key it is handed and compares: equal means the reply provably names that message, and collision resistance means nobody can craft a different “parent” with the same digest.

Hash as a checkable commitment. When a reply is sent, the parent message account key is hashed with blake3 and the digest is stored in the reply as reply_to_hash. When the bounty is claimed, the program hashes the presented account key again and requires the two digests to match. 1. Sending the reply — commit to the parent's key bountied message account 7gWc3P…J8kQhx blake3 stored in the reply account: reply_to_hash = 057b93…13ba3a must match 2. Claiming the bounty — the program recomputes and compares the same account key, taken from the claim's account list blake3 057b93…13ba3a anyone can recheck the link; nobody can forge a different parent with the same digest

Why blake3 specifically

The protocol needs a modern cryptographic hash; blake3’s particular shape fits unusually well:

  • The digest is exactly 32 bytes — precisely Solana’s maximum PDA seed length, so a digest drops into a seed slot with no truncation (truncating a digest weakens it) and no padding.
  • It is fast in a single pass with no key setup, which suits hashing many short names — and its construction has no length-extension weakness, so a digest can be published as an identity without the ceremony (double-hashing, HMAC) that raw SHA-256 needs in some commitment patterns.
  • One implementation everywhere. blake3’s reference implementation is pure Rust and no_std-friendly, so the exact same crate compiles into the on-chain programs’ SBF bytecode and into every host-side tool (the CLI, the gRPC gateway, the mail servers). The programs hash in-program rather than through a syscall, and client and chain agree byte-for-byte because they are literally running the same code. (Where the protocol must use SHA-256 — the DNSSEC proof verifier, whose record digests are fixed by the DNS RFCs — the programs use Solana’s sol_sha256 syscall instead; blake3 is SithBit’s own choice for naming and linkage.)
  • It hashes as a tree. Internally blake3 splits input into 1 KiB chunks and combines their hashes as a binary Merkle tree, which is what lets large inputs hash in parallel and supports verified streaming. SithBit’s inputs are far too small to exercise this, but it is the design that gives blake3 its speed headroom and its name.
blake3's internal Merkle tree: the input is split into 1 KiB chunks, each chunk is hashed independently, pairs of chunk hashes are combined into parent hashes, and the parents fold into a single 32-byte root digest. chunk 0 (1 KiB) chunk 1 chunk 2 chunk 3 h0 h1 h2 h3 parent(h0, h1) parent(h2, h3) 32-byte root digest

To be plain about history: the codebase does not record a written selection rationale for blake3 (its own glossary entry just calls it “the fast hash”). The properties above are why it fits, not a quoted design memo.

Where blake3 appears in the protocol

What is hashedWhat the digest becomesChapter
Alias name (lowercased, domain-stripped)The alias account’s PDA seed; the transfer-escrow and marketplace-listing PDAs reuse the same digest under their own seed prefixesAliases
Domain name (lowercased)The domain account’s PDA seed; pending-deactivation and domain-listing PDAs reuse itDomains
Normalized from addressThe frombox PDA seed, and the from_hash stored in every message account — the plaintext address never reaches the chainFromboxes, Sending mail
Bountied message account key (base58 text)reply_to_hash in the reply — the privacy-preserving claim linkageReply bounties
Claimed domain (lowercased)Part of the proof-witness buffer’s PDA seed, so each claimant stages into their own deterministic bufferAuthorize a domain by proof
Attesting domain (lowercased)Part of the sender-attestation PDA seed, combined with the attested wallet — one attestation per (domain, wallet) pairAttest a verified sender
Postmaster ceremony wallet (raw 32-byte pubkey)A leaf in the postoffice’s commitment_root Merkle tree; the sorted-pair interior nodes fold up to that root, and a membership proof re-derives it leaf-by-leafPostmaster key custody

Further reading