Part II · Retracing the ancestors This chapter contains one line of cryptography. Everything else is systems design. The prologue’s claim starts getting cashed here.
In 1991 Stuart Haber and Scott Stornetta were trying to solve a modest problem:
How do you prove a document existed at a particular moment and has not been altered since?
Their answer is the first brick in this book that is not cryptography.
(Satoshi cites them more often in the whitepaper than he cites anyone else.)
4.1 A block is two things¶
import hashlib
def h(s: str) -> str:
'''SHA-256, returned as a hex string.'''
return hashlib.sha256(s.encode()).hexdigest()
class Block:
def __init__(self, data, prev_hash):
self.data = data # this block's content
self.prev_hash = prev_hash # the previous block's hash (a pointer)
def hash(self):
# The trick: hash BOTH data and prev_hash.
# So a block's fingerprint "contains its history".
return h(self.data + self.prev_hash)
# The first block has no predecessor -- use 64 zeros as a placeholder (the genesis block).
genesis = Block("genesis block", "0"*64)
print("genesis hash:", genesis.hash()[:16], "...")
genesis hash: eb7eb7c66dfeb692 ...
Open up Block and there is nothing mysterious in it:
blk.data = "Alice pays Bob" # a string
blk.prev_hash = "eb7eb7c66d...4a1f" # another string, 64 characters
blk.hash() = h(data + prev_hash) # concatenate them, hash onceTwo fields and one method.
All of the cleverness in this chapter lives in that one line of hash(): it hashes
not just this block’s data, but the previous block’s fingerprint as well.
That single move makes every block’s fingerprint contain the entire history behind it.
(The first block has no predecessor, so prev_hash is 64 zeros. That is the genesis block —
real Bitcoin’s works the same way, except Satoshi stuffed that newspaper headline into its data.)
4.2 Link them up¶
chain = [Block("genesis block", "0"*64)]
for d in ["Alice pays Bob", "Bob pays Carol", "Carol pays Dave"]:
prev = chain[-1].hash() # hash of the last block
chain.append(Block(d, prev)) # nail this one behind it
for i, blk in enumerate(chain):
print(f"block{i}: data={blk.data:<16} prev={blk.prev_hash[:10]}... hash={blk.hash()[:10]}...")
block0: data=genesis block prev=0000000000... hash=eb7eb7c66d...
block1: data=Alice pays Bob prev=eb7eb7c66d... hash=073bc9e1df...
block2: data=Bob pays Carol prev=073bc9e1df... hash=0a5190f7bc...
block3: data=Carol pays Dave prev=0a5190f7bc... hash=220b015c68...
Do not let the word “chain” become abstract. It is a Python list:
[block0, block1, block2, block3]Every element is a Block object, and every object’s prev_hash field holds the hash of the
one before it. That is what strings them together.
Once it runs, check one thing in the output:
each row’s prev= is exactly the previous row’s hash=.
block0: data=genesis block prev=0000000000... hash=eb7eb7c66d...
block1: data=Alice pays Bob prev=eb7eb7c66d... hash=073bc9e1df...
^^^^^^^^^^ identical to the line above
block2: data=Bob pays Carol prev=073bc9e1df... hash=0a5190f7bc...
^^^^^^^^^^ again
block3: data=Carol pays Dave prev=0a5190f7bc... hash=220b015c68...“Chain” is not a magic word. It means: each block writes the previous block’s fingerprint into its own body.
4.3 Why it can’t be edited¶
Now write the checker. It asks exactly one question: does each block’s stored prev-hash still equal the real hash of the block before it?
def check(chain):
'''Has the chain been altered?'''
for i in range(1, len(chain)):
if chain[i].prev_hash != chain[i-1].hash():
return False, i # break detected at block i
return True, None
print("is the chain valid?:", check(chain))
is the chain valid?: (True, None)
Prints (True, None).
Four lines, and not one of them requires any cryptographic knowledge — it compares two strings for equality. It works entirely because of chapter 1’s avalanche effect.
Play the attacker¶
# Now be the attacker: quietly inflate block 1.
print("before:", chain[1].data)
chain[1].data = "Alice pays Bob one million" # rewrite history
print("after :", chain[1].data)
print("still valid?:", check(chain)) # should be False
before: Alice pays Bob
after : Alice pays Bob one million
still valid?: (False, 2)
Prints (False, 2) — the break was detected at block 2.
Walk through what happened, one step at a time:
You changed block 1’s
dataBlock 1’s
hash()is computed on demand, so it changed immediately (avalanche, chapter 1)But block 2’s
prev_hashwas recorded earlier. It did not change.At
i=2,checkfindschain[2].prev_hash != chain[1].hash()→ break
This is what “immutable” actually means: not that you can’t change it, but that you can’t change it without it showing.
That distinction deserves insisting on. The chain has no mechanism that prevents editing —
the line chain[1].data = ... executed perfectly well and Python did not object. What it
achieves is: once you’ve done it, anyone can detect it in four lines of code.
This is the first appearance of a pattern that repeats through the whole book:
Bitcoin almost never forbids anything. It makes cheating detectable, or unprofitable.
4.4 Pull one thread and the whole thing unravels¶
The attacker will not give up there. He’ll try to patch it.
# YOUR TURN:
# 1) The attacker "repairs" block 2's prev_hash to match block 1's new hash.
# Try it -- and then block 3 breaks.
chain[2].prev_hash = chain[1].hash() # patch block 2
print("after patching block 2:", check(chain)) # block 3 is still broken -- why?
# 2) To make the whole chain valid again, he must recompute EVERY later block.
def rebuild_from(chain, start):
for i in range(start, len(chain)):
chain[i].prev_hash = chain[i-1].hash()
return chain
# rebuild_from(chain, 2)
# print("after full rebuild:", check(chain))
after patching block 2: (False, 3)
Question 1 prints (False, 3) — the break moved from block 2 to block 3.
Why? Because patching block 2’s prev_hash also changed block 2’s hash()
(hash() covers data + prev_hash, so touching either one changes the fingerprint).
Now block 3’s stored prev_hash no longer matches.
Plug one hole and the next one opens. All the way to the end of the chain.
Question 2’s rebuild_from does make check return True again — the attacker wins.
But notice what it cost him: he had to recompute every single block from the edit point to the tip.
Editing block 1 meant redoing 3 blocks. If the chain were a hundred thousand blocks long and he edited block 5, he would have to redo more than 99,000 of them.
Here is Bitcoin’s central intuition in embryo: Editing one old block forces you to recompute every block after it. Now suppose recomputing a single block is expensive. Then rewriting older history costs more and more. The older the record, the safer it is.
And making “recompute one block” expensive is exactly what the next chapter does.
Haber and Stornetta built the chain. Adam Back built the expense. Satoshi put them together.
4.5 The ledger¶
| What you wrote | Field |
|---|---|
h() — SHA-256 | Cryptography (chapter 1’s, one line, nothing new) |
class Block — data plus the previous hash | Systems design |
| “each block writes the previous fingerprint into itself” | Systems design |
check() — tamper detection | Systems design |
rebuild_from() — the attacker’s repair | The attacker’s view |
For the first time, the ledger tips to the right.
The only cryptography in this chapter is h(), and even that was imported from chapter 1.
Everything else — linking blocks, detecting tampering, understanding the cascading cost —
is systems design.
This is the prologue’s claim showing up as code for the first time:
The genuinely original part is not the cryptography. It is everything else.
Hashing was a brick somebody else made. “Use hash pointers to turn data into a self-verifying history” is an idea, and it required no new mathematics whatsoever.
Next¶
Proof-of-work.
You will build the expense described above: a puzzle that is hard to solve and trivial to check.
Then you get to be a miner, and watch the number of attempts climb as you add difficulty.
Along the way you’ll find something that surprises most people: mining is far luckier than you’d think. That fact explains why real Bitcoin sometimes produces two blocks in two minutes and sometimes makes you wait forty.