Part V — Blockchain & Trustless Consensus · Chapter 1

Cryptographic primitives for decentralization

Hook

Thousands of computers, none of them trusting each other, need to agree on one shared ledger — and instantly notice if a single character in a single past transaction gets changed. No central server is checking. What makes that even possible?

Intuition
root577
↑ toyHashPair(parent₀, parent₁)
parent 0741
parent 1229
↑ toyHashPair(leaf, leaf)
alice pays bob 3459
bob pays carol 1416
carol pays dav…636
dave pays alic…976

Every transaction gets hashed. Every pair of hashes gets combined into a new hash, one level up. Do that until only one hash is left — the root. Nothing here is fixed in place; if any transaction below changes, the specific hash sitting above it changes too, and so does everything above that.

Formalize

This Part shares one concrete, deterministic hash function everywhere it's needed: toyHash(input), which returns a number in [0,1000)[0, 1000), and toyHashPair(left, right), which combines two child hashes into a parent's. Real hash functions (SHA-256) return 256-bit numbers instead of one in [0,1000)[0,1000), but behave the same way qualitatively — deterministic, and a one-character change in the input scrambles the output unpredictably (the avalanche effect).

  1. Hash every transaction

    Leaf ii's hash is hi=toyHash(txi)h_i = \text{toyHash}(\text{tx}_i) — one hash per transaction, independent of every other transaction.

  2. Combine pairs going up

    Each parent combines two children: pi=toyHashPair(h2i,h2i+1)p_{i} = \text{toyHashPair}(h_{2i}, h_{2i+1}). With 4 transactions that gives 2 parents.

  3. Repeat until one hash remains — the root

    The root is the parents' parent: root=toyHashPair(p0,p1)\text{root} = \text{toyHashPair}(p_0, p_1). A block's entire set of transactions is now represented by exactly one number.

  4. A digital signature binds one signer to one message

    A signature is a value only the holder of a private key can produce for a given message, and anyone who knows the corresponding public key can check. This chapter's toy version, toySign(m,k)\text{toySign}(m, k), is symmetric rather than truly asymmetric — but it demonstrates the operational property that matters: change the key, or the message, and verification fails.

  • txi\text{tx}_i — the ii-th transaction, as raw text.
  • hih_i — that transaction's leaf hash, toyHash(txi)\text{toyHash}(\text{tx}_i).
  • pip_i — a parent hash, combining two children with toyHashPair\text{toyHashPair}.
  • root — the single hash at the top, standing in for every transaction below it at once.
Play
root577
↑ toyHashPair(parent₀, parent₁)
parent 0741
parent 1229
↑ toyHashPair(leaf, leaf)
alice pays bob 3459
bob pays carol 1416
carol pays dav…636
dave pays alic…976

root = 577 — Alice's signature over the original message still verifies ✓

Tamper with transaction 0 and watch two things break at once: the Merkle root changes (anyone recomputing it from the transactions sees a mismatch instantly), and Alice's signature — computed over the original wording — no longer verifies against the tampered one. Two independent tripwires over the same data.

Worked example

Four fixed transactions: alice pays bob 3, bob pays carol 1, carol pays dave 2, dave pays alice 1.

  1. Hash each leaf
    • h0=toyHash("alice pays bob 3")=459h_0=\text{toyHash}(\texttt{"alice pays bob 3"})=459
    • h1=toyHash("bob pays carol 1")=416h_1=\text{toyHash}(\texttt{"bob pays carol 1"})=416
    • h2=toyHash("carol pays dave 2")=636h_2=\text{toyHash}(\texttt{"carol pays dave 2"})=636
    • h3=toyHash("dave pays alice 1")=976h_3=\text{toyHash}(\texttt{"dave pays alice 1"})=976
  2. Combine into parents
    • p0=toyHashPair(459,416)=741p_0=\text{toyHashPair}(459,416)=741
    • p1=toyHashPair(636,976)=229p_1=\text{toyHashPair}(636,976)=229
  3. Combine into the root

    root=toyHashPair(741,229)=577\text{root}=\text{toyHashPair}(741,229)=577.

  4. Now tamper with transaction 0

    Change it to alice pays bob 9:

    1. Its leaf hash becomes 465465 instead of 459459 — a one-character change, an unrelated-looking output
    2. Parent 0 becomes toyHashPair(465,416)=808\text{toyHashPair}(465,416)=808 instead of 741741
    3. Parent 1 is untouched, still 229229, since it doesn't depend on h0h_0
    4. The root becomes toyHashPair(808,229)=301\text{toyHashPair}(808,229)=301 — nowhere near the original 577577, even though only one transaction out of four changed
Checkpoint

Click any one transaction below to tamper with it (a character gets appended), and watch the root change from its original value of 577.

root577
↑ toyHashPair(parent₀, parent₁)
parent 0741
parent 1229
↑ toyHashPair(leaf, leaf)
Click a transaction to tamper with it
Summary
hi=toyHash(txi),pi=toyHashPair(h2i,h2i+1),root=toyHashPair(p0,p1)h_i = \text{toyHash}(\text{tx}_i), \qquad p_i = \text{toyHashPair}(h_{2i}, h_{2i+1}), \qquad \text{root} = \text{toyHashPair}(p_0, p_1)

A Merkle tree compresses any number of transactions into one root hash, and changing any transaction anywhere in the tree changes the root — no central authority has to re-check every transaction by hand; everyone just compares one number. Digital signatures add the other half: proof that a specific party authorized a specific transaction in the first place. The next chapters build an entire consensus mechanism — proof-of-work — on top of exactly this hash function.