Skip to content

Smart Contracts & the EVM

Bitcoin Script made one decision louder than any other: it is deliberately not Turing-complete. No loops, no unbounded recursion, hard caps on size — so every script provably halts and every node can afford to verify it. Ethereum looked at the exact same danger and made the opposite call: build a full virtual machine that can loop, and tame the halting problem with money instead of with a ban. This page is about that machine — the Ethereum Virtual Machine (EVM) — what it lets you do that Script won’t, the mechanism (gas) that keeps it safe, and the real, recurring cost of choosing expressiveness.

Strip away the hype and a smart contract is simple: it is a contract account (from the previous page) whose behaviour is fixed code, plus persistent storage, living at an address on the ledger.

CONTRACT ACCOUNT
├── code : EVM bytecode, set once at deploy time, immutable thereafter
├── storage : its own key→value map, which the code can read and rewrite
└── balance : it can hold and send ETH like any account

You call a contract by sending it a transaction. Its code runs on every node, deterministically, reading and mutating its storage, possibly moving funds or calling other contracts. The output is a new world state that every node computes identically. That last clause is the whole game: a smart contract is only useful as shared truth if thousands of strangers’ machines all run it and agree on the result — the same convergence requirement that has driven this entire book.

What does this unlock that Bitcoin Script can’t express? Programs that remember and evolve: a token whose balances live in contract storage, an exchange that holds a pool of two assets and re-prices them on every trade, a loan that accrues interest block by block, an auction that tracks the high bid. Bitcoin Script can encode rich spending conditions (multisig, timelocks, hashlocks — see Script), but it cannot hold an evolving counter or a running market. The account model plus a looping VM is what makes that natural.

Turing-completeness: the power and the bomb

Section titled “Turing-completeness: the power and the bomb”

Bitcoin forbids loops because of one nightmare: a lock with an infinite loop would make every validating node hang forever — a free, global denial-of-service — and you cannot tell in advance whether an arbitrary program halts (the classic halting problem).

The EVM allows loops anyway. So how does it avoid the nightmare? Not by solving the halting problem — nobody can — but by making every step cost money and capping how much money a transaction may spend. Run too long, and you simply run out of budget and get stopped.

Every EVM operation has a fixed gas price — a unit of computational cost. Cheap ops (add two numbers) cost a little; expensive ops (writing to storage, which every node must persist forever) cost a lot. A transaction carries two numbers:

gas limit : the MOST units of work you authorize (your fuel tank size)
gas price : how much you'll pay per unit, denominated in gwei (1 gwei = 10^-9 ETH)
fee you pay = gas actually used × gas price

As the EVM executes, it deducts gas op by op. Two outcomes:

  • It finishes within the limit → state changes commit; you pay for the gas actually used.
  • It hits the gas limit first → execution reverts (all its state changes are undone, as if it never ran) — but you still pay for the gas it burned. The work was really done by every node, so it must be paid for even though the result is thrown away.

That second branch is the whole defense. An attacker’s infinite loop doesn’t hang the network; it spins until the transaction’s gas runs out, then reverts — and the attacker is charged for every wasted step. The economic floor makes the attack self-limiting.

while (true) { x = x + 1 } ← legal EVM code, would loop forever
reality: each iteration burns gas → gas limit reached → REVERT
attacker pays for ~millions of useless steps, network never hangs

Under the hood — the EVM as a 256-bit stack machine

Section titled “Under the hood — the EVM as a 256-bit stack machine”

The EVM is, like Bitcoin Script, a stack machine — but a vastly more powerful one. It works in 256-bit words (chosen to fit a Keccak-256 hash and a full account balance in one slot), and unlike Script it has:

  • Loops and jumps (JUMP / JUMPI) — control flow that can repeat, the thing Script forbids.
  • Persistent storage (SLOAD / SSTORE) — the contract’s own key/value map that survives between calls; Script has no memory between transactions at all.
  • Inter-contract calls (CALL / DELEGATECALL) — a contract can invoke another, composing behaviours. Powerful, and the source of whole classes of bugs (see the caution below).
  • Gas accounting baked into the interpreter — every opcode deducts from the remaining gas before it runs.

Contracts are usually written in a high-level language — Solidity is the dominant one, Vyper a stricter alternative — and compiled down to this bytecode. The result is genuinely a small global computer: deterministic, replicated across every node, and metered so it can’t run away.

Bitcoin’s answer, and why both can be right

Section titled “Bitcoin’s answer, and why both can be right”

Put the two philosophies side by side:

Bitcoin ScriptEthereum / EVM
Turing-complete?No — no loops, bounded by designYes — loops allowed
Halting handled byForbidding the dangerous constructPricing it (gas) and capping the budget
Holds evolving state?No (spending conditions only)Yes (contract storage)
Attack surfaceTiny, easy to reason aboutLarge — every contract is new code that can be buggy
What it optimizes forCheap, universal, guaranteed-finishing verificationMaximum expressiveness as a global computer

Neither column is “the mistake.” Bitcoin’s base layer has one job — let strangers cheaply agree on who owns what — and for that job, fewer ways to surprise a verifier is a virtue; richer behaviour is pushed to higher layers like Lightning. Ethereum chose to be a world computer, accepting a much larger surface of things that can go wrong (buggy contracts, re-entrancy, exploits) as the price of programmability. The EVM doesn’t beat Script; it answers a different question.

How does a world computer still let untrusting strangers agree on one ledger? The same way Bitcoin does — by making the answer to “what happened?” something every node computes identically and deterministically — but with one extra invention to make a looping machine safe to replicate: gas. Without a halting bound, a programmable ledger couldn’t exist, because no one could agree on (or afford) the result of code that might run forever. Gas restores the property strangers need — bounded, deterministic, universally checkable execution — for programs far richer than Script allows. Ethereum didn’t abandon the convergence requirement; it paid a fuel-metered toll to keep it while turning the ledger into a computer. The cost is a far larger surface of things that can break, which the next pages — and the whole history of smart-contract exploits — keep returning to.

The EVM-plus-gas combination is a chosen answer to a danger Bitcoin handled the opposite way:

  • Why does it exist? Ethereum built a Turing-complete VM where Bitcoin Script deliberately won’t — the EVM exists to run programs that remember and evolve (tokens, markets, loans block by block), which bounded, loop-free Script can’t express.
  • What problem does it solve? The halting problem, economically: rather than forbidding loops (Bitcoin’s answer), the EVM prices every opcode in gas and caps the budget — non-termination becomes unaffordable, not impossible. Gas also prices the real burden of permanent state growth (~20,000 gas for a new storage slot vs ~3 for arithmetic).
  • What are the trade-offs? A far larger attack surface — every contract is fresh, possibly-buggy code (The DAO’s reentrancy drained ~3.6M ETH and forced the ETH/ETC split) — and an out-of-gas transaction still pays for the work that gets reverted.
  • When should I avoid it? A programmable base layer is the wrong call when “fewer ways to surprise a verifier” matters more than expressiveness — Bitcoin keeps L1 minimal and pushes richer logic to Lightning; a looping VM is overkill for plain ownership transfer.
  • What breaks if I remove it? Without gas, a looping VM can’t exist on a shared ledger — no one could agree on (or afford) the result of code that might run forever — so gas is exactly what restores bounded, deterministic, universally checkable execution.
  1. In account terms, what is a smart contract? Name the three things a contract account holds.
  2. Bitcoin forbids loops to avoid an infinite-loop denial-of-service. How does the EVM allow loops without the same catastrophe?
  3. What happens to a transaction’s state changes — and to its fee — when it runs out of gas mid-execution? Why is the fee still charged?
  4. Why are storage-writing opcodes among the most expensive, and what real-world cost does that gas price reflect?
  5. Using The DAO (June 2016), explain the new kind of risk that Turing-completeness introduces, and how the response split Ethereum into two chains.
Show answers
  1. A smart contract is a contract account whose behaviour is fixed code living at an address. It holds code (immutable EVM bytecode set at deploy), storage (its own key→value map the code can read and rewrite), and a balance (it can hold and send ETH). You call it by sending it a transaction; its code runs on every node and produces a new world state all nodes compute identically.
  2. Not by solving the halting problem (impossible) but by metering — every opcode costs gas, and a transaction caps how much gas it may spend. A loop that runs too long simply runs out of gas and is stopped, so non-termination becomes unaffordable rather than forbidden. The network never hangs.
  3. All of its state changes revert (are undone as if it never ran), but the gas it burned is still paid. The fee is charged because every node really did execute that work, so it must be compensated even though the result is discarded — that economic floor is what makes infinite-loop attacks self-limiting.
  4. Writing storage (SSTORE) forces every node to persist that data essentially forever, so it carries the heaviest real cost; a new slot is ~20,000 gas vs ~3 for a simple arithmetic op. The gas schedule roughly tracks each op’s true burden on the network — especially permanent state growth — so gas prices the externality an unbounded state would otherwise dump on every node.
  5. The DAO bug was in the contract’s own code (a reentrancy flaw — it sent funds before updating its balance, letting the attacker re-enter and drain ~3.6 million ETH), not in the EVM. That new attack surface — every contract is fresh, possibly-buggy code — is exactly what Turing-completeness opens up. The response split the chain: most of the network hard-forked to reverse the theft (today’s Ethereum / ETH), while a minority kept the original ledger on the “code is law” principle (Ethereum Classic / ETC).