Key Principle
"The EVM is the part of Ethereum that handles smart contract deployment and execution" — a sandboxed, single-threaded, 256-bit stack machine with no system/hardware interface ("completely virtual"). It resembles the JVM/.NET model: a host-agnostic runtime executing its own bytecode, abstracting only computation and storage. The 256-bit word size was chosen "mainly to facilitate native hashing and elliptic curve operations." Its only job is to compute valid state transitions over the world state — a map of 160-bit (20-byte) addresses to accounts, each holding balance (wei), nonce, storage, and code. EOAs have no code and empty storage; only contract accounts have both.
Why This Matters
Quasi-Turing-completeness = gas as the halting bound. The EVM is "quasi" because every execution is capped at a finite number of steps by the gas available. The reason is the halting problem — "we can't tell, just by looking at a program, whether it will take forever or not to execute." A single-threaded world computer with no scheduler, handed an infinite loop, would seize up for everyone. Gas converts the undecidable "will it terminate?" into a pre-paid budget; the ceiling is the block gas limit. Gas is not merely a fee — it is what makes a Turing-complete shared computer safe to run.
Volatile vs. nonvolatile is the gas split. Three data regions: immutable program-code ROM (the bytecode), volatile memory (zeroed each call), and permanent storage (part of Ethereum state). The memory-vs-storage split is the gas split: MLOAD/MSTORE are cheap and discarded; SLOAD/SSTORE are the expensive resource because storage mutates the permanent world state every node must keep forever — "the price internalizes the externality of bloating world state."
The gas-cost hierarchy (durable; specific integers are historical):
- Pure stack arithmetic/logic ≈ near-free (ADD/SUB/LT/AND ≈ 3; MUL/DIV/MOD ≈ 5).
- Persistent storage dominates:
SSTOREto a fresh slot ~20,000;SLOAD~200. - Reading other accounts is costly:
BALANCE~400,EXTCODESIZE/EXTCODECOPY~700. - Hashing/logging carry overhead:
SHA3~30;LOG0375 rising ~375 per indexed topic toLOG41875. CREATE32,000 — the priciest single op. Sending a transaction = 21,000 gas.- Call-family gas is "Complicated" (computed at runtime), so static gas estimates are only approximate for any contract making external calls or writing fresh storage.
Gas cost vs. gas price. Cost = units of gas an operation requires (fixed, deterministic). Price = ether paid per unit. Separating them lets the market float the ether↔computation relationship while keeping operation cost invariant to ether's price swings. transaction fee = total gas used × gas price.
Good Examples
Opcode categories (256-bit stack machine; opcodes pop operands, push results; all arithmetic mod 2^256, 0^0 = 1):
- Arithmetic —
ADD MUL SUB DIV SDIV MOD SMOD ADDMOD MULMOD EXP SIGNEXTEND, plusSHA3. Silent overflow is why defensive contracts need SafeMath-style checks. - Stack/memory/storage —
POP,MLOAD/MSTORE,MSTORE8,SLOAD/SSTORE,MSIZE,PUSHx(1–32 bytes),DUPx/SWAPx(1–16) — all 3 gas. - Control flow —
STOP JUMP JUMPI PC JUMPDEST. Jumps may only land on aJUMPDEST, making bytecode statically analyzable. - System —
LOGx CREATE CALL CALLCODE RETURN DELEGATECALL STATICCALL REVERT INVALID SELFDESTRUCT.DELEGATECALLruns another account's code while preservingmsg.senderand value — basis of upgradeable proxies and the Parity multisig disaster.STATICCALLforbids state changes (safe read-only calls). - Environmental —
ORIGIN(originating EOA) vsCALLER(immediate caller) is the distinction behind "never authorize on tx.origin." - Block —
BLOCKHASH(last 256 blocks only — why it is a poor randomness source),COINBASE TIMESTAMP NUMBER DIFFICULTY GASLIMIT.
Out-of-gas vs. REVERT semantics. A triggering transaction instantiates a fresh EVM (PC=0, storage loaded, memory zeroed, gas budget = what the sender paid) that mutates a throwaway copy of world state, committing to real state only on clean completion. Hitting zero gas throws Out of Gas: execution halts, the sandbox is discarded, no state change persists — except the nonce increment and ether already paid to the block producer. "Out of gas still costs real money — gas pays for resources consumed, not for success." By contrast, REVERT halts, undoes state, and returns remaining gas + a reason string (unlike the old throw/INVALID which burned all gas).
Deployment vs. runtime bytecode. A creation transaction sets to = 0x0, data = deployment bytecode. The EVM runs that code; its output becomes the account's code. The constructor runs once at deploy time then vanishes — never lives on-chain. Runtime bytecode (a strict subset) is all that executes on later calls. Get each with solc --bin (deployment) vs solc --bin-runtime (runtime).
ABI dispatcher and 4-byte selectors. Incoming calldata hits the contract's dispatcher. A function's selector is the first 4 bytes of keccak256 of its canonical signature — keccak256("withdraw(uint256)") → 0x2e1a7d4d (canonical = uint256, not uint). The dispatcher: (1) checks calldata ≥4 bytes, else jumps to fallback; (2) loads 32 bytes, isolates the top 4 by DIV; (3) EQ-compares against each precomputed selector and JUMPIs to the match. Malformed short calldata hits the fallback rather than a function.
Counterpoints
- Failure still costs ether: "the sender will be charged a transaction fee, as miners have already performed the computational work up to that point and must be compensated."
- Storage refunds (negative gas):
SELFDESTRUCTrefunded 24,000 gas;SSTORE[x] = 0(nonzero→zero) refunded 15,000 — capped at half the total gas used to stop deletion-profit attacks. - Mispriced opcodes are an attack surface: a 2016 gas/real-cost mismatch nearly halted mainnet, fixed by Tangerine Whistle (EIP-150).
- Block gas limit caps per-block throughput; a tx whose own gas exceeds the limit can never be mined. Miners vote to adjust it by ≤1/1024 per block.
Key Quotes
"This makes the EVM a quasi–Turing-complete machine: it can run any program you feed into it, but only if the program terminates within a particular amount of computation." — Antonopoulos & Wood, Chapter 13
"Although the transaction was unsuccessful, the sender will be charged a transaction fee, as miners have already performed the computational work up to that point and must be compensated for doing so." — Antonopoulos & Wood, Chapter 13
Rules of Thumb
- Minimize
SSTORE/storage writes; computation is cheap, persistent state is the dominant cost. - Treat the gas hierarchy as durable but specific integers as historical (repriced repeatedly).
- Static gas estimates are only approximate for call-heavy or fresh-storage contracts — set limits with margin or pay for nothing on OOG.
- Prefer
REVERT(returns gas + reason) over patterns that burn all gas. - Authorize on
CALLER(msg.sender), neverORIGIN(tx.origin). - Debug deployed contracts via runtime bytecode (
solc --bin-runtime); constructor logic is gone. [DATED 2018]:DIFFICULTY→block.prevrandao(Merge); gas repriced over forks (EIP-150/1884/2200/2929); EIP-1559 (2021) replaced sender-set gasPrice with burned base-fee + tip; refunds reduced/removed by EIP-3529.
Related References
- Oracles — Importing Trusted Data into a Deterministic EVM - determinism/no-RNG forces imported trust
- DApps and the web3 Stack — Decentralization Across the Whole Architecture - gas/immutability push logic off-chain
- Smart Contract Vulnerability Catalog - DELEGATECALL, tx.origin, overflow, reentrancy
- Consensus — Rules Without Rulers (PoW, PoS, Forks) - block gas limit and the world-state convergence