Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Part I · Bricks: the cryptographic floor By the end of this chapter you will have a function that fingerprints any data, and a guessing game — built out of that function — that nobody can cheat at.

Cryptography is, from beginning to end, an argument about bytes. So before touching a single big word, spend ten minutes getting comfortable with something small: the same thing can be written down several different ways.

One thread runs under this whole chapter, and the next seventeen keep returning to it:

representation → one-wayness → composition


1.0 Four costumes, one thing

Here is a string, and here is what a computer actually has when it holds one.

string     : café42
bytes      : b'caf\xc3\xa942'
byte count : 7 bytes
hex        : 636166c3a93432
as integer : 27973116691559474

Four lines of output, and they are four costumes on one body:

  • café42 — the way you read it

  • b'caf\xc3\xa942' — what is actually in memory: 7 bytes for 6 characters

  • 636166c3a93432 — those same 7 bytes, two hex digits each, so 14 characters

  • 27973116691559474 — all 7 bytes read as a single binary number

Notice the 7. Six characters, seven bytes. The é takes two. Text is not a sequence of letters as far as a computer is concerned; it is a sequence of bytes, and how many bytes a character costs depends on the encoding. This trips people up constantly, and it is the first thing that has to go.

Do not think of hex as “another encoding.” It is the same bytes, printed differently — the way Celsius and Fahrenheit are the same temperature. Every TXID, every address, every public key you have ever seen on a block explorer works this way: bytes underneath, hex on the screen.

That is idea number one: representation. Change the costume, the body is unchanged.

YOUR TURN

bytes: b'caf\xc3\x00\xa942'

The exception here is the content, not a bug. Uncomment the decode() and you will almost certainly get a UnicodeDecodeError, because I zeroed out a byte in the middle of that é.

Which gives us the chapter’s first real conclusion:

Not every string of bytes is valid text. But every string of bytes can be hashed.

A hash does not care whether you feed it English, Chinese, a JPEG, or corrupted garbage. It only sees bytes. That indifference is exactly why it can be used everywhere later.


1.1 Hashing: easy in, impossible out

Now give the idea a picture.

A blender. Put an apple in, press the button, get juice. Nobody staring at the juice can reconstruct the apple — how big it was, how many seeds. But blend the same apple again and you get the same juice.

Apple → juice: trivial. Juice → apple: impossible. That is what “one-way” means.

A fingerprint. Billions of people, no two prints alike. Hand someone a print and they cannot tell you who it belongs to; bring them a suspect and they can tell you instantly whether it matches. Hashing takes a fingerprint of data.

Why “can’t go back” means impossible, not just hard

This deserves an argument, because a lot of people assume hashes are merely unbroken so far. They are not.

A hash squeezes inputs of any length into a fingerprint of one fixed length. The word “hello” and the complete works of Shakespeare come out the same size: 32 bytes.

Infinitely many possible inputs, finitely many possible outputs. So many different inputs must collide onto the same fingerprint — that is the pigeonhole principle, not an engineering weakness. Given a fingerprint alone, there is no unique answer to recover.

This is not about computing power. The information is gone.

(And this cleanly separates hashing from encryption. Encryption is a one-to-one map — nothing is lost, so with the key you can go back. Hashing is many-to-one — information is destroyed, so there is no “decrypt” operation to speak of. That distinction becomes important in 1.2.)

Run it

We use SHA-256 from hashlib, Bitcoin’s workhorse hash.

fingerprint (hex): d19e9ea45283a659b53117c0614bad5d1774d96fd8587ae3c61ca8893c68b030
length           : 32 bytes = 256 bits
hex characters   : 64

One detail is easy to skim past: sha256 takes bytes and returns bytes. msg came from .encode(), and h is bytes too — h.hex() exists only so you can look at it. We keep this habit for the rest of the book: compute in bytes, display in hex.

The output says 32 bytes = 256 bits. That 256 is where the name comes from.

Two properties you have to see for yourself

short input  -> 32 bytes
21000 chars  -> 32 bytes

café42 -> d19e9ea45283a659b53117c0614bad5d1774d96fd8587ae3c61ca8893c68b030
café43 -> 10fc7d999b3f8c15cc23ab43fdc00b9a2d8656f5509ba25b02b07f93a27c474a
hex characters that differ: 64 / 64

Two things in that output.

First, the two lengths are identical. A 2-character input and a 21,000-character input both produce 32 bytes. That is the pigeonhole argument made visible: you just watched the infinite get crushed into the finite.

Second, diff comes out at 64 out of 64. Every single character changed. One digit different in the input, and the output is unrecognizable. This is the avalanche effect.

A number worth doing arithmetic on

Avalanche is usually described as “flip one bit and half the output changes.” That is true — but it is talking about bits, not the characters on your screen.

We just got 64 out of 64. That is 100%, not half. Why?

Because one hex character is four bits. For the character to look unchanged, all four bits must be unchanged. If each bit flips with probability one half:

P(a hex character survives) = (1/2)⁴ = 1/16 so the expected number that change is 64 × 15/16 = 60

We got 64, which is a slightly lucky draw above the expected 60. And if you count at the bit level, you find 130 out of 256 — 50.8%, clean as advertised.

Both numbers are correct; they measure different things. This is the first time in this book that two true statements look contradictory because the units don’t line up, and it will not be the last (chapter 5’s difficulty, chapter 10’s fee rates). Build the habit now: always ask what the percentage is a percentage of.

Avalanche is not decorative. It is the foundation of everything later. Because of it, you cannot “get warmer” — flip one bit and the result lands somewhere completely unrelated, with no gradient to climb. The reason mining in chapter 5 can only be brute force starts here.

YOUR TURN

Question 3 is not filler. Every a1b2c3d4...e5f6a7b8 identifier you will ever see — including the abbreviated transaction IDs on any block explorer — is produced exactly like that.

One more property, and it sounds boring

b',\xf2M\xba_\xb0\xa3\x0e&\xe8;*\xc5\xb9\xe2\x9e\x1b\x16\x1e\\\x1f\xa7B^s\x043b\x93\x8b\x98$'

These are always equal.

sha256 is a pure function: same input, same output, forever. No randomness, no side effects, no clock, no machine dependence.

That sounds like nothing. It is the entire reason the next section works.


1.2 Commitment: lock now, reveal later

A commitment is not a new brick. It is a way of using the hash you already have — your first taste of composition.

The problem it solves

You and a friend play a guessing game. You think of a number from 1 to 100; they guess.

The hole is obvious. They guess “50,” and nothing stops you from saying “I was thinking 51, you lose.” There is no referee.

How a commitment closes it: at the start, you publish the hash of your number — write it on the board. After the guess, you publish the number itself, and everyone re-hashes it and compares.

Three properties make it work:

  • Hiding — from the fingerprint they cannot recover the number (one-wayness)

  • Binding — after you reveal, they can check (pure function: recomputing must match)

  • Caught — swap in a different number and the fingerprints don’t match

The part people miss

A commitment never needs to be “opened.”

You volunteer the original; it is not extracted from the fingerprint. A hash can never produce its input — and a commitment never asked it to.

So the same property, “you can’t go back,” is fatal for encryption and essential for commitment.

That property flips sides several more times in this book. In chapter 3 it is a weakness (use a hash as an identity and it burns after one use — which is precisely what forced signatures to be invented). In chapter 11 it is the tool that lets one 32-byte root commit to an entire block. In appendix B it is what saves hashes from quantum computers.

Whether a property is a flaw or a feature depends entirely on where you put it.

Three lines

1. published commitment: 73475cb40a568e8d ...
2. honest reveal       : True
3. cheating reveal     : False

Before reading the output, look at the shape of these two functions, because “commitment” sounds far more elaborate than it is:

  • commit(42) returns c, which is a 64-character string: 73475cb40a568e8d...

  • verify(42, c) recomputes the hash and compares with ==

That’s it. A commitment is a hash plus a rule about timing.

The three output lines are the three possible situations: True is honesty, False is getting caught, and that c on the first line is a value from which nobody can extract anything.

This is composition. Hashing only knows how to make fingerprints. But arrange when the fingerprint is published and when the original is published, and you have a capability that did not exist before.

Play a full round

committed: 031b4af5197ec30a ...   (the answer is not visible here)
you guessed: 50
the answer was     : 51
commitment intact? : True
did you win?       : False

The second-to-last line is always True, whether you guessed right or not.

Because it is not checking whether you won. It is checking whether the setter cheated. Those are two separate lines of code and two separate questions, and everyday language smashes them together constantly.

A commitment makes no promise that the outcome is fair. It promises the process was not altered.

The only way to make that line print False is to pass a different number to verify — i.e. to simulate the setter changing their story.


1.3 The radius of trust

So far a commitment looks like a party trick for keeping a friend honest. Its real weight is somewhere else: it lets “you can’t cheat me” reach further and further.

Level one — face to face, enforced by watching. You and your friend are in the same room. Worried they’ll change their answer? Watch them. They write it, seal it in an envelope, you don’t blink.

Enforcement mechanism: physical supervision.

Level two — a city apart, enforced by a hash. They’re far away and you can’t watch. So they send you the hash first (the commitment); after you guess, they send the number, and you recompute it yourself.

Enforcement mechanism: the math watches for you.

Level three — the whole world, enforced by anyone. They broadcast the hash to a crowd of strangers. After the reveal, every one of them can check independently. Nobody has to trust the person at all.

Enforcement mechanism: anyone can verify.

In one sentence:

A commitment moves “you can’t cheat me” from “I have to watch you” (physical), to “math watches for me” (at a distance), to “anyone can watch you” (globally). You stop needing to trust a person and only need to trust arithmetic anyone can redo.

That last step is Bitcoin’s soul. The remaining seventeen chapters are, in a sense, one long exercise in pushing that sentence to a larger scale — from a guessing game to a global monetary system.

But one thing must not get blurred

Bitcoin uses that idea. It does not use commitments to implement it.

CommitmentSpending bitcoin
How you prove itPublish the hidden value (reveal)Sign with the private key
Does the secret appear?Yes — and it’s spent the moment it doesNever, not once
How many times can you do it?OnceUnlimited

You need to spend money repeatedly, so the private key can never be published. That rules out the commitment approach entirely — reveal it once and it’s finished.

Bitcoin uses signatures: sign with the private key, let the world verify with the public key, and the private key never appears. Spend ten thousand times, and it still never appears.

Commitments teach us “anyone can verify.” Signatures implement it far more cleverly.

That trick — verifying without revealing — is chapter 3. Before that, chapter 2 has to turn “private key” and “public key” from nouns into numbers you compute yourself.


1.4 The ledger

Every chapter ends by counting: of what you just wrote, what was cryptography and what wasn’t.

What you wroteField
sha256() — a one-way fingerprintCryptography
commit() / verify() — lock first, check laterCryptography, applied (composition, not a new primitive)
“Publish the hash first, the value second” — the orderingProtocol design

Almost pure cryptography. Easy tally. Enjoy it — it doesn’t stay this way.

From chapter 4 the right-hand column fills rapidly with systems, consensus, game theory, economics. By chapter 7 you will write an entire chapter of code and find, looking back, not one line of cryptography in it.

(Only two later chapters detour back: chapter 11’s Merkle trees and chapter 13’s real curve. The other thirteen stay on the right.)

Three ideas to carry forward:

  1. Representation — many costumes, one body. Bytes are the thing; hex is for humans.

  2. Pure function — same in, same out. This is why a commitment can be trusted, and ultimately why an entire chain can be independently verified by anyone.

  3. Composition — commitment = hash + a rule about timing. Big capabilities out of small primitives. The rest of this book does nothing but this.


Next

Finite fields and elliptic curves.

You will build a clock with exactly 97 positions, find a curve living on it, and start hopping from a fixed point.

How many hops you took is your secret. Where you landed is your public key.

Then you will write a brute-forcer and crack your own private key — and see precisely why the same code, on a real curve, would still be running when the sun burns out.