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 V · Toward real engineering · the final chapter Every “deficiency” in chapter 14’s virtual machine was a decision.

In chapter 14 you built a script VM that probably felt crude: no loops, no variables, no functions, not even an if.

If it’s a VM, why not make it stronger?

The answer needs exactly what you started up last chapter:

Every full node has to validate every transaction. A script an attacker sends for a few cents must not be able to freeze the entire network.


18.1 What a validation machine has to guarantee

When Bitcoin nodes validate a script, they need four properties:

1. It will definitely stop.
2. Every node computes the same result.
3. The cost has an upper bound.
4. It does not read or write state that differs between machines.

Each of those four takes away a capability that ordinary programming languages have. Here they are one at a time.

First, a VM with limits:

 1. 2        stack=[2]
 2. 3        stack=[2, 3]
 3. OP_ADD   stack=[5]
 4. 5        stack=[5, 5]
 5. OP_EQUAL stack=[True]
(True, 'done, steps=5')

Compared with chapter 14’s VM, two things are new: max_steps and max_stack_items.

Those parameters are not defensive-programming hygiene. They are part of the consensus rules — every node must use the same limits, or one accepts what another rejects and the chain splits (chapter 9).


18.2 Why no loops

If Script had loops, someone could write this:

while True:
    do nothing

In an ordinary program that is merely a bad program, and it freezes your own machine.

In Bitcoin it is a bad program broadcast to every node on Earth.

(False, 'stopped for safety after 20 steps -- possible infinite loop')

That infinite loop is two tokens long.

[0, "OP_JUMP"] — the attacker pays fees by the byte, so he pays for two bytes and every node on the planet loops forever.

This is an extraordinarily asymmetric attack: cost to attack, O(1). Cost to defend, O(∞).

Bitcoin’s choice: there is no OP_JUMP, no while, no for. A script runs left to right and ends by construction.

Chapter 14’s for item in script: — it cannot go backwards and it cannot jump. That was not laziness. That was the design.


18.3 Why Turing completeness is a defect here

Turing complete means, roughly: the language is powerful enough to simulate any computation.

That is a virtue in a general-purpose language. It is not a virtue when any stranger can hand you code to execute.

More power -> harder to predict runtime
More power -> harder to bound resources
More power -> easier to hide an attack

And more fundamentally: deciding whether a Turing-complete program halts is undecidable (the halting problem). There is no checker you can write that rejects all looping scripts in advance.

Which leaves two roads:

  • Restrict the language so it halts by construction → Bitcoin

  • Charge for every step and stop when the money runs out → Ethereum’s gas

Bitcoin Script’s philosophy: not general computation. Predictable validation only.


18.4 Why no global mutable state

Suppose a script could read “globals” — how many transactions are in the mempool, what time it is, the price on some website.

The problem: different nodes do not see the same world. (Section 12.1, and you watched it directly last chapter.)

node A validation result: True
node B validation result: False

The same transaction is valid for node A and invalid for node B.

Neither node is malfunctioning — they simply see different worlds. But the result is a network split, and the kind from section 9.5 that does not heal by itself.

So script validation must depend on exactly two things: the transaction itself, and data already settled on the blockchain.

Determinism is not a code-quality preference. It is a precondition for consensus.

(This also explains something that confuses newcomers: Bitcoin’s timelocks use block height or the timestamp in the block header, never the node’s own system clock. The first two are written on the chain and identical for everyone; the last differs on every machine.)


18.5 Why limit opcodes

Verifying a signature costs tens of times more than an addition. An attacker can build a script stuffed with signature checks.

cheap_script cost: 2
expensive_script cost: 2400
1000 expensive scripts in one block: 2400000

This is the answer to section 14.4’s question: P2PKH does OP_EQUALVERIFY (a hash comparison, cheap) before OP_CHECKSIG (a signature check, expensive) — do the cheap check first and bad scripts are rejected before any real money is spent.

Real Bitcoin caps script size, operation count, and total signature checks. Not because those features are uncool, but because network-wide validation has to stay affordable.

Put a number on it

cost per tx         10 -> block cost         20,000, about      0.20 seconds
cost per tx      1,000 -> block cost      2,000,000, about     20.00 seconds
cost per tx    100,000 -> block cost    200,000,000, about   2000.00 seconds
cost per tx 10,000,000 -> block cost 20,000,000,000, about 200000.00 seconds

Look at the last row: 200,000 seconds to validate one block. More than two days.

If validating a block takes minutes, let alone hours, nodes cannot keep up. Nodes that fall behind drop out, and people who drop out stop running their own node and start trusting somebody else’s —

and “ordinary people can no longer afford to run a node” is precisely how decentralization dies.

That is the most important sentence in this chapter. Every one of these limits ultimately protects not CPU cycles but the property that one ordinary computer can independently verify the whole system.

Chapter 1’s “anyone can check the arithmetic” comes full circle here: if verification becomes expensive, that sentence becomes a slogan.


18.6 Why some opcodes were removed

An ordinary software bug gets patched. A consensus bug is a different animal:

If node versions disagree about one script -> the chain splits.
If one opcode can consume unbounded memory -> every node is crushed.
If an opcode's behaviour is loosely specified -> upgrading becomes dangerous.
doubling 10 times -> length about 1,024
doubling 20 times -> length about 1,048,576
doubling 30 times -> length about 1,073,741,824
doubling 40 times -> length about 1,099,511,627,776

Forty doublings is one terabyte. Under a hundred bytes of script demanding a terabyte of memory from every node on the network.

That is the flavour of resource explosion: an operation that looks completely harmless — joining two strings — amplifying exponentially in the absence of a hard cap.

Satoshi disabled a batch of opcodes in 2010, and OP_CAT was among them.

(Worth noting: OP_CAT has been seriously discussed for re-enablement in recent years, because inside Tapscript it is already bounded by a 520-byte stack element limit, which makes the doubling attack above impossible. The same opcode is differently dangerous under different resource limits — which shows that “dangerous” was never a property of the opcode itself, but of its combination with the limit system around it.)


18.7 So why can Ethereum be more powerful?

Some systems choose Turing completeness. They generally must introduce resource pricing:

Every step costs gas.
Gas runs out, the program stops.
Complex computation costs more money.
(True, 'done, gas remaining = 60')
(False, 'out of gas, halted')

Gas is a different solution, not a worse one.

It converts “will this halt,” which is undecidable, into “is there enough money,” which is computable. The price is that every operation must be priced — and mispricing is a vulnerability. (Ethereum has in fact suffered DoS attacks from underpriced opcodes and had to reprice them in a hard fork.)

Bitcoin Script    : conservative, predictable, cheap to validate, anyone can run a node.
General contract VM: expressive, but requires careful metering and resource limits.

This is not right versus wrong. The design goals differ. Bitcoin ranks “an old laptop can verify the entire network” above “you can write complicated contracts.” That is a value judgment, not a technical verdict.

YOUR TURN

Question 4 is the last exercise this book leaves you, and it is real: the network you started in chapter 17 runs no scripts at all inside valid_tx(). Wire chapter 14’s VM into it and you will have to answer every question in this chapter.


18.8 The ledger

What you wroteField
run_limited() — a VM with capsConsensus safety
run_with_jump() — a two-byte infinite loopThe attacker’s view
script_reads_mempool() — non-determinismConsensus safety
script_cost() / estimate_block_validation()Resource economics
run_with_gas() — the other roadComparative design

Zero cryptography.

RestrictionWhat it prevents
No loopsscripts that never stop
Not Turing completeunpredictable validation time
No global mutable statenodes computing different results
Capped opcodes, script size, signature checksCPU and memory DoS
Dangerous opcodes disabledbugs becoming consensus splits

The sentence:

Bitcoin Script’s goal is not “you can write anything.” Its goal is: a spending condition sent by a stranger can be validated by every node in the world quickly, deterministically, and cheaply.

It is a pocket knife, not a general-purpose computer. And that was chosen.


Closing the books

Eighteen chapters. Here is the whole tally:

ChWhat you builtField
1Hashing, commitmentsCryptography
2Finite fields, elliptic curves, public keysCryptography
3ECDSA signing and verificationCryptography
4The hash time-stamp chainSystems
5Proof-of-workSystems + probability
6UTXOs, transactions, double-spend preventionSystems
7Blocks, longest-chain consensusSystems + game theory
8Block rewards, halving, incentivesEconomics
9Forks and upgradesSystems + consensus + social coordination
10Fees, difficulty adjustmentEconomics + control theory + statistics
11Merkle treesCryptography
12Mempool, fee market, confirmationsNode policy + economics + consensus
13secp256k1, the real curveCryptography
14The Script VMSystems + a small VM
15HD walletsKey management + human factors
16Hardware signingSecurity engineering
17A multi-node networkDistributed systems
18Script’s safety limitsConsensus safety + resource control

Eighteen chapters. Five of cryptography.

And what those five contain — hashing, elliptic curves, signatures, Merkle trees — not one of them was invented by Satoshi. They come from papers published in 1976, 1979 and 1991.

The other thirteen chapters are Bitcoin.

Those thirteen all answer the same question: how do mutually distrustful strangers with no central authority agree on who owns what? For thirty years, people held the same bricks and nobody built the house.

The prologue’s claim is now yours to judge:

Cryptography was his brick supply. Building a bank with no manager out of those bricks was the invention.


What you can do now

The book is over. The chain is yours.

  • Add back what was left out: section 17.6 lists six items. Wire them into toycoin_network and you have a crude but structurally complete node.

  • Use real formats: transaction serialization, Base58/Bech32 addresses, real Script bytecode.

  • Go to signet or testnet: run experiments with mature libraries — and now you know what they are doing underneath.

  • Follow the frontier: Taproot, Schnorr signatures, and proposals like CTV, CSFS and CAT. Sections 14.5 and 18.6 already pushed that door open a crack.

Three appendices are roads you may choose not to take, but should know exist:

  • Appendix A: the cryptography this book skipped — encryption, zero-knowledge proofs, MPC

  • Appendix B: why a quantum computer breaks public keys but not hashes

  • Appendix C: CHECKSIG vs CSFS — same lamp, different piece of paper


The bricks are borrowed and the house is original — and this time, you’re the one who built it.