EVM Deep Dive (Part 4): Understanding Ethereum's "World State"

·

The Ethereum Virtual Machine (EVM) is the beating heart of the Ethereum blockchain, executing smart contracts and maintaining consensus across a decentralized network. In this article, we’ll explore one of its most foundational concepts: Ethereum’s “world state”—a dynamic, global snapshot of all account balances, contract code, and storage. We’ll dive into the Go Ethereum (Geth) codebase to trace how individual contract storage operations like SSTORE and SLOAD are rooted in this broader world state.

This is part four of our EVM Deep Dive series. If you haven’t read Part 3, where we explored contract storage internals, we recommend reviewing it first for context.


Ethereum Architecture Overview

To understand the world state, we begin with Ethereum’s block architecture—a hierarchical structure that ensures data integrity and verifiability across nodes.

At the core of each block is the block header, which contains metadata and cryptographic commitments to three major datasets:

These roots allow any node to verify the correctness of the entire blockchain state without storing every detail locally—a key feature enabling decentralization and scalability.

👉 Discover how blockchain states are secured through cryptographic hashing


The Block Header: Foundation of Trust

Each Ethereum block header includes critical fields that anchor the network’s trust model. Let’s examine some key components:

In the Geth source code (core/types/block.go), the Header struct mirrors these fields exactly. Among them, State Root stands out as the anchor point for understanding how individual accounts and their storage fit into the global picture.

Any change in a single account’s balance or contract storage triggers a cascade of hash updates—from the account level up to the state root—ensuring tamper-evidence at every layer.


What Is the World State?

Ethereum’s world state is not stored directly in blocks. Instead, it’s a logical construct maintained by nodes, represented as a Merkle Patricia Trie (MPT)—a cryptographically secure key-value store.

This trie structure allows efficient verification: lightweight clients can confirm an account’s balance or contract code using only a small proof, without downloading the full state.

While we won’t delve deeply into MPT mechanics here, it’s important to recognize that this structure enables trustless synchronization and state validation across thousands of nodes worldwide.


Ethereum Account Structure

Every Ethereum account—whether externally owned (EOA) or contract-based—is represented by four essential fields:

  1. Nonce:

    • For EOAs: Number of transactions sent.
    • For contracts: Number of contracts created.
      Prevents replay attacks and ensures transaction ordering.
  2. Balance:
    The account’s current balance in wei, where 1 ETH = 10¹⁸ wei.
  3. Code Hash:

    • For contracts: Hash of deployed bytecode.
    • For EOAs: Hash of an empty string (since they have no code).
      This field is immutable—once set, it cannot change.
  4. Storage Root:
    A Merkle root pointing to another Patricia Trie that holds the contract’s internal storage—mapping 256-bit keys to 256-bit values.

These fields are defined in Geth’s state_account.go file under the StateAccount struct, aligning precisely with Ethereum’s yellow paper specifications.


Storage Root: Where Contract Data Lives

Beneath the Storage Root lies a second Merkle Patricia Trie—dedicated exclusively to a contract’s storage space.

When a contract modifies its storage via SSTORE, this trie updates, changing the Storage Root. That change then propagates upward:

SSTORE → Storage Trie Update → New Storage Root → New State Root

This cascading effect ensures that any alteration to contract data is reflected globally in the blockchain’s state root—making tampering immediately detectable.


StateDB, stateObject, and StateAccount: Geth's Internal State Management

To manage state transitions during transaction execution, Geth uses three core structures:

1. StateAccount

A static representation of an Ethereum account—its nonce, balance, code hash, and storage root.

2. stateObject

Represents a mutable instance of an account during transaction processing. It wraps a StateAccount and tracks pending changes.

3. StateDB

The top-level interface for reading and modifying state. It maintains a cache of stateObjects and handles persistence via tries.

These layers work together to provide a sandboxed environment where contract executions can safely modify state before finalization.


Initializing a New Ethereum Account

When a new contract is deployed, Geth creates a fresh StateAccount via the createObject method in statedb.go.

Here’s how it works:

  1. StateDB.createObject(address) is called.
  2. A new stateObject is instantiated with an empty StateAccount.
  3. The data field of stateObject holds this new account.
  4. Fields like dirtyStorage are initialized to track future modifications.

This newly created object exists in memory until changes are committed to the underlying trie database.


SSTORE: Writing to Contract Storage

The SSTORE opcode writes data to a contract’s storage slot. Here’s how it flows through Geth:

  1. From instructions.go, opSstore pops two values from the stack:

    • loc: The storage slot key (256-bit).
    • val: The value to store (256-bit).
  2. These are passed to StateDB.SetState(contractAddr, loc, val).
  3. If no stateObject exists for the address, one is created.
  4. The SetState method on stateObject:

    • Checks if the value has changed.
    • Appends an entry to the journal (for rollback capability).
    • Updates dirtyStorage, a map of pending changes: hash → hash.
💡 The journal enables EVM-level reversibility—if a transaction reverts, all state changes can be undone cleanly.

Eventually, during the commit phase:

👉 Learn how smart contract storage impacts gas efficiency and security


SLOAD: Reading from Contract Storage

The SLOAD opcode retrieves a value from a storage slot:

  1. Pops loc (the slot key) from the stack.
  2. Calls StateDB.GetState(addr, loc).

The lookup follows a priority order:

  1. dirtyStorage: Most recent in-memory changes (from current tx).
  2. pendingStorage: Committed but unflushed writes.
  3. originStorage: Original values from the trie (persistent state).

This layered approach ensures that reads reflect the latest logical state—even within complex transactions modifying the same slot multiple times.

For example:
If SSTORE(slot, A) precedes SLOAD(slot) in a transaction, the latter returns A directly from dirtyStorage, bypassing slower disk lookups.


Frequently Asked Questions

Q: What is the “world state” in Ethereum?

A: The world state is a global mapping of all Ethereum addresses to their account data (nonce, balance, code, storage). It’s not stored in blocks but derived from the state root in each block header using a Merkle Patricia Trie.

Q: Why doesn’t Ethereum store the full world state in blocks?

A: Storing the entire state would make blocks enormous and unsustainable. Instead, only the state root (a hash) is included. Nodes reconstruct the state locally using tries and historical data.

Q: How does SSTORE affect gas costs?

A: SSTORE has variable gas costs depending on whether you’re setting a zero to non-zero (20K+ gas), modifying an existing value (5K gas), or clearing storage (refunds apply).

Q: Can external accounts have storage?

A: No. Only contract accounts have non-empty storage roots. Externally owned accounts (EOAs) have empty code and storage hashes.

Q: What happens to dirtyStorage after a transaction commits?

A: During the commit phase, changes in dirtyStorage are applied to the underlying Merkle trie. Once persisted, the new storage root updates the account and eventually becomes part of the block’s state root.

Q: How do light clients verify state without storing everything?

A: Light clients use Merkle proofs. Given a state root and a specific account or storage slot, they can verify correctness using a small subset of trie nodes—enabling trustless verification on low-power devices.


Final Thoughts

Understanding Ethereum’s world state—and how operations like SSTORE and SLOAD interact with it—is crucial for developers building secure, efficient smart contracts. By tracing these mechanisms through Geth’s implementation, we see how low-level opcodes connect to high-level consensus guarantees.

In future articles, we’ll explore advanced opcodes like CALL and DELEGATECALL, diving into delegate-based execution and cross-contract interactions.

👉 Start building and testing smart contracts with real-time blockchain tools