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 III · The house: building a chain This is where cryptography runs out.

You have hand-written every borrowed brick. From here on, nobody handed Satoshi a blueprint.

And the first thing that happens is that you hit a wall.


6.1 Fold the signature into a tool

You wrote ECDSA by hand in chapter 3. This chapter is about systems, so the signature code gets folded up and used as a tool without further comment.

signature tools ready.

Nothing in that cell is new — it is chapter 3’s code compressed. Three functions to remember:

  • make_key(priv) → returns (priv, pub)

  • sign(priv, msg) → signs with the private key

  • verify(pub, msg, sig) → verifies with the public key alone

(Note that sign here gives k a default of k=11. Section 3.6 explained why that is a fatal mistake in a real system — two messages signed with the same k and the private key falls out. It is written this way purely so your output matches the book’s. Never do this in real code.)


6.2 What is money, structurally?

Here is a question that sounds trivial: in your ledger, what data structure is “money”?

The obvious answer is a dictionary mapping public key → balance. Bitcoin does not do that.

In Bitcoin, money is not an account balance. It is a pile of unspent transaction outputs (UTXOs) — each one like a banknote you cannot tear, marked “this much, belongs to this public key.”

Spending means destroying old notes and issuing new ones.

Alice's note id: utxo1
Alice's balance: 10
the ledger's utxos: {'utxo1': ((0, 21), 10)}

That L.utxos line matters. Its shape is roughly:

{'utxo1': (Alice's public key, 10)}

Meaning: the ledger currently recognizes exactly one unspent note, called utxo1, owned by Alice, worth 10.

Every transaction from here on just edits that dictionary: delete old notes, insert new ones.

Also notice balance() is computed, not stored — it walks every UTXO and adds up the ones that are yours. In Bitcoin, “balance” is not a field. It is the result of a query. That design choice comes back in chapter 15 (why wallets have to scan) and chapter 12 (why “dust” is a real problem).


6.3 What a transaction looks like

signed message: c6ee49246480cc72 ...
Alice's signature: (18, 22)
tx.inputs : ['utxo1']
tx.outputs: [((15, 6), 6), ((0, 21), 4)]
object fields: {'inputs': ['utxo1'], 'outputs': [((15, 6), 6), ((0, 21), 4)], 'sig': (18, 22)}

In plain English, this transaction says:

  • inputs=['utxo1'] — I want to spend Alice’s original 10-coin note

  • outputs=[(Bob's key, 6), (Alice's key, 4)] — issue two new notes: 6 to Bob, 4 back to Alice

  • sig — Alice signs it, proving the old note really is hers to spend

Pause on the change. Alice only wants to give Bob 6, but the note in her hand is worth 10. A UTXO cannot be torn in half — it must be spent whole. So she destroys the entire 10 and issues herself a fresh 4.

This is exactly why your Bitcoin wallet keeps producing “change addresses” you never asked for. It is not the wallet being clever. It is a direct consequence of the UTXO model.


6.4 How the ledger validates a transaction

apply runs four checks. Count them first: only one is cryptography.

Alice->Bob accepted?: True  new notes: ['utxo2', 'utxo3']
Alice: 4  Bob: 6
utxos after: {'utxo2': ((15, 6), 6), 'utxo3': ((0, 21), 4)}

It works. Alice’s 10-coin note is consumed and becomes Bob’s 6 plus Alice’s 4 in change.

Before: {'utxo1': (Alice, 10)} After: utxo1 is gone, and utxo2 -> (Bob, 6) and utxo3 -> (Alice, 4) appear.

That is the UTXO model in one line: money is not a number in an account, it is a stack of notes nobody has spent yet.

Now go back to the four checks and label each one:

CheckThe questionField
1. Is the note still thereWas it already spentSystems / double-spend prevention
2. Is the signature validIs this the ownerCryptography
3. Is value conservedAre coins being inventedSystems
4. ApplyDelete old, insert newSystems

One out of four. Remember that ratio — the next section makes it starker.


6.5 The climax: what stops a double-spend?

Alice tries to cheat. She already spent that 10-coin note to Bob. Now she tries to spend it again, to Carol.

Before reading on, guess: what is going to stop her?

is the double-spend's signature valid: True
does the ledger accept it?: False
reason: note utxo1 does not exist or was already spent (double-spend!)

Read those two lines carefully.

The signature is valid. The transaction is rejected anyway.

Alice’s signature is completely legitimate — she really is the owner of that note, she really is entitled to sign for it, and chapter 3’s mathematics has nothing to complain about.

What stops her is not cryptography. It is the ledger noticing the note is gone.

The control experiment

To make this unmistakable, run the opposite attack: Carol tries to steal Bob’s coin.

Carol tries to steal Bob's coin: False
reason: invalid signature -- not the owner   <- this time cryptography caught it

False, because the signature is invalid.

Two attacks, two defences:

AttackWhat caught itField
Stealing someone else’s coin (forgery)Signature verification failedCryptography
Spending your own coin twiceThe ledger saw the note was goneSystems

Cryptography guards ownership. The system guards against reuse.

These sound like the same job. They are completely different — and for thirty years, everything anyone built had only the first defence.


6.6 But this ledger has a fatal hole

That apply looks like it solved double-spending. It did not.

Because L exists on one computer.

Now picture Alice doing this:

  • On machine A, she spends the note to Bob

  • At the same moment, on machine B, she spends the same note to Carol

Each machine has its own Ledger object. In each machine’s own ledger, both transactions are entirely legal — signature valid, note present, value conserved. Both calls to apply return True.

Neither machine can detect anything wrong.

The bug is not in the code. The bug is this:

“That note has already been spent” presupposes there is one ledger everyone agrees on. If there are many ledgers telling different stories, what does “already” even mean?

Deeper still: both double-spending transactions carry genuine signatures. Cryptography can prove Alice signed both. It has no capacity whatsoever to decide which of Alice’s two conflicting payments came first.

Double-spending is, at bottom, an ordering problem for conflicting transactions. And ordering requires a group of mutually distrustful people to agree on one sequence.

That is not a cryptography problem. A stronger signature algorithm does not move you one inch closer.

This is where Chaum stalled (his answer was to add a central bank). This is where b-money and Bitgold stopped, in papers. Thirty years, everyone stuck on this page.

YOUR TURN

Please actually stop on question 2. Not being able to answer it is the correct response — it took the human race thirty years.


6.7 The ledger

What you wroteField
Ledger.utxos — money as unspent notesSystems design
class TX — inputs, outputs, signatureSystems design
apply check 1 (is the note still there)Systems / double-spend prevention
apply check 2 (is the signature valid)Cryptography (imported from chapter 3)
apply checks 3–4 (conservation, application)Systems design

One of the four checks is cryptography, and that one was borrowed.

But this chapter’s real conclusion is not in the table. It is in the hole:

You wrote every line of code in this chapter, and double-spending is still unsolved. Because solving it requires something that is not in your toolbox — and your toolbox contains only cryptography.


Next

Blocks and longest-chain consensus.

Alice will fork two chains from two machines, and both will be entirely valid. Then you will write a function to settle between them.

That function is one line. It does not check signatures, does not ask who anyone is, does not care who is right — it only compares who burned more electricity.

And afterwards you will notice something: chapter 7 contains no cryptography at all, from beginning to end.

That is what chapter 1 promised you.