Ethereum & Solidity

Chapter 2
Ethereum Architecture and the EVM

Diploma in Blockchain
Ethereum & Solidity   •   Diploma

Ethereum Architecture and the EVM

Introduction

Ethereum is a distributed computing platform that combines a peer-to-peer network, a replicated state machine, a consensus mechanism, and a programmable execution environment. Unlike a conventional database, Ethereum does not depend on one trusted server. Instead, many independent nodes maintain and verify a common state. Transactions request state changes, validators order those transactions into blocks, and every execution-capable node deterministically checks the resulting state transition.

Ethereum architecture is best understood through two closely related layers:

  • The execution layer, which handles transactions, smart contracts, accounts, the Ethereum Virtual Machine (EVM), gas accounting, and state transitions.
  • The consensus layer, which coordinates validators, block proposal, attestations, fork choice, and finality under proof-of-stake.

The separation of these layers is a major feature of contemporary Ethereum. The execution layer determines what the state becomes; the consensus layer determines which ordered block history is accepted by the network.

Learning Objectives

After completing this chapter, a learner should be able to:

  1. Explain the relationship between Ethereum’s execution and consensus layers.
  2. Describe Ethereum’s state-transition model and the contents of a block.
  3. Distinguish accounts, world state, storage, memory, calldata, and bytecode.
  4. Explain how Merkle Patricia tries commit to Ethereum state.
  5. Describe EVM opcodes, the stack, gas, memory, and persistent storage.
  6. Explain ABI encoding and the relationship between Solidity functions and calldata.
  7. Analyze why EVM execution is deterministic and identify important limitations.

1. Ethereum as a Replicated State Machine

Let the Ethereum world state at time t be represented by St. A valid transaction is an input Tt that, when processed under the protocol rules, produces a new state:

St+1 = Υ(St, Tt, Bt)

Here, Υ is the Ethereum state-transition function, Bt is the block context, and St+1 is the resulting world state. The notation is conceptual: actual Ethereum execution also involves transaction validity checks, gas rules, withdrawals, and block-level processing.

A transaction may transfer ether, deploy contract bytecode, or invoke an existing contract. Every validating node independently executes the transaction. If honest nodes begin with the same state and apply the same protocol rules, they should obtain the same result. This property is called deterministic execution.

Ethereum therefore provides a shared state machine rather than merely a payment ledger. The state includes account balances, contract code, contract storage, and account nonces. A transaction changes this state only if it satisfies protocol requirements such as correct signature, nonce, gas constraints, and sufficient funds.

2. Execution and Consensus Layers

The execution layer

The execution layer is responsible for:

  • Maintaining the accounts and contract state.
  • Validating and executing transactions.
  • Calculating gas consumption.
  • Producing execution receipts and logs.
  • Constructing the execution payload included in a beacon block.
  • Computing the post-execution state root.

Execution clients implement these rules. Examples include Geth, Nethermind, Besu, and Erigon.

The consensus layer

The consensus layer is responsible for:

  • Managing proof-of-stake validators.
  • Selecting block proposers.
  • Organizing slots and epochs.
  • Collecting validator attestations.
  • Applying fork-choice rules.
  • Establishing economic finality through justified and finalized checkpoints.

Consensus clients include Lighthouse, Prysm, Teku, Nimbus, and Lodestar. A complete Ethereum node normally uses both an execution client and a consensus client, connected through a standardized engine interface.

Ethereum consensus and execution layers Consensus layer • Validators and attestations • Proposers and fork choice • Slots and epochs • Justification and finality • Beacon block structure Execution layer • Transactions and accounts • EVM computation • Gas and receipts • Contract state and logs • State and receipts roots engine API payload validity Accepted chain state and finalized history
Relationship between Ethereum consensus and execution layers
Figure: Relationship between Ethereum consensus and execution layers

Figure: The consensus layer coordinates accepted history, while the execution layer computes state changes within that history.

The layers are separate but not independent. Consensus cannot accept an execution payload unless the execution client confirms that it is valid. Conversely, execution requires an agreed block context and transaction ordering supplied through consensus.

3. Accounts and the World State

Ethereum has two principal account types.

Account typeCodeStorageTypical control
Externally owned account (EOA)NoneNonePrivate-key signature
Contract accountPresent after deploymentPresent when usedEVM code and calling accounts

An account is described by four conceptual fields:

  • Nonce: a counter used to prevent replay and distinguish transactions or contract creations.
  • Balance: the account’s ether balance, denominated in wei.
  • Storage root: a commitment to the contract’s persistent key-value storage.
  • Code hash: a hash identifying the account’s deployed runtime bytecode; EOAs have no contract code.

The world state is a mapping from 160-bit account addresses to account records. Ethereum does not store this mapping as a simple flat file in the protocol. Instead, it commits to the mapping using a modified Merkle Patricia trie.

The state root is placed in the block’s execution payload. A node can therefore verify that a claimed state corresponds to a particular root without receiving every account record from another node. A proof contains the relevant trie nodes and allows a verifier to reconstruct the path from a key to its value.

4. Merkle Patricia Tries

A Merkle Patricia trie combines three ideas:

  1. A trie, which organizes keys by their nibbles (four-bit hexadecimal units).
  2. Path compression, which avoids storing long chains of nodes with only one child.
  3. Merkle hashing, in which a parent hash commits to its child references and encoded contents.

Ethereum uses several logically distinct tries, including:

  • The global state trie, committing to accounts.
  • A storage trie for each contract, committing to that contract’s storage slots.
  • A transaction trie for transactions in a block.
  • A receipt trie for transaction receipts in a block.

A simplified relationship is:

StateRoot = H(RootNodestate)

where H denotes the protocol’s cryptographic hash function and RootNodestate is the encoded root node of the state trie. This is a conceptual representation; Ethereum’s actual trie encoding and hash treatment depend on node type and protocol rules.

The trie is not a database encryption mechanism. It provides authenticated commitments: a root hash makes unauthorized changes detectable, but confidentiality must be provided by other mechanisms. Trie proofs also prove inclusion or exclusion relative to a known root; they do not by themselves establish that the root belongs to the canonical chain unless the block is trusted or validated.

5. Blocks and Transaction Processing

A block contains consensus information and an execution payload. Important execution-related fields include:

  • Parent block reference.
  • Block number and timestamp.
  • Gas limit and gas-related accounting fields.
  • Fee recipient.
  • Ordered transactions.
  • Execution state root.
  • Receipts root.
  • Logs bloom.
  • Base fee and, where applicable, data-gas parameters.

A transaction is processed broadly as follows:

  1. Decode the transaction and recover its signature.
  2. Identify the sender and verify nonce, balance, and fee constraints.
  3. Establish the block and transaction execution context.
  4. Deduct or reserve applicable costs.
  5. Execute the transaction or contract creation in the EVM.
  6. Commit successful state changes, or revert state changes while preserving specified effects such as gas accounting and logs behavior according to protocol rules.
  7. Produce a receipt and update the state root.

Transaction order matters. If two transactions modify the same storage slot, reversing their order can produce a different final state. Consensus therefore supplies not only a set of transactions but an accepted sequence.

6. The Ethereum Virtual Machine

The EVM is a deterministic, quasi-Turing-complete virtual machine. It executes runtime bytecode, not Solidity source code. Solidity is compiled into EVM bytecode, and the bytecode consists of one-byte opcodes, sometimes followed by immediate data.

The EVM is a stack machine with 256-bit words. A word is 32 bytes, and arithmetic operations are generally performed modulo 2256 unless an opcode specifies different behavior. For example, an addition that exceeds the maximum unsigned 256-bit value wraps according to modular arithmetic:

(a + b) mod 2256

This rule applies to low-level EVM arithmetic, although Solidity’s checked arithmetic behavior may insert overflow checks depending on compiler version and code context.

Important machine components

ComponentPurposeLifetime
StackOperands and intermediate valuesCurrent call frame; maximum depth 1,024 items
MemoryTemporary byte-addressed workspaceCurrent call; expanded as needed
StoragePersistent contract key-value dataAcross transactions
CalldataRead-only input supplied to a callCurrent call
Return dataOutput returned by a callCurrent call context
Program counterPosition of next instructionCurrent execution
Gas counterResource budgetCurrent transaction/call execution

The stack is last-in, first-out. Most opcodes consume values from the top and push results back. The EVM has no general-purpose registers.

Bytecode and opcodes

Examples of opcode families include:

  • PUSH: place an immediate value on the stack.
  • POP: remove a stack item.
  • ADD, SUB, MUL, DIV: arithmetic.
  • MLOAD, MSTORE, MSTORE8: memory operations.
  • SLOAD, SSTORE: persistent storage access.
  • CALL, DELEGATECALL, STATICCALL: message calls.
  • JUMP, JUMPI, JUMPDEST: control flow.
  • RETURN, REVERT, STOP, SELFDESTRUCT: termination-related operations.
  • LOG0 through LOG4: event log emission.

An opcode has a defined stack effect, gas cost, and semantic result. The actual gas schedule is protocol-defined and can change through Ethereum upgrades; developers should not assume that an old gas estimate remains permanently valid.

7. Gas and Resource Accounting

Gas is a unit of computational resource accounting. A transaction specifies a gas limit and fee parameters. The EVM charges gas for operations, memory expansion, calldata-related costs, storage changes, and other protocol-defined work.

A simplified fee relationship is:

TransactionFee = GasUsed × EffectiveGasPrice

The variables are:

  • GasUsed: the gas consumed by execution, measured in gas units.
  • EffectiveGasPrice: the price paid per gas unit, measured in wei per gas.
  • TransactionFee: the resulting fee, measured in wei.

The formula applies after the effective price has been determined from the transaction’s fee parameters and the block’s base fee. On proof-of-stake Ethereum, the base fee is burned, while a priority fee generally goes to the validator and other accounting rules apply to the fee components. The exact treatment depends on transaction type and protocol rules.

Gas serves two important purposes: it limits computation and compensates the network for resource consumption. It also prevents an indefinitely running computation from blocking every node.

8. Memory, Storage, Calldata, and Code

Memory

Memory is temporary and byte-addressable. It begins conceptually empty for a call and does not persist after the call completes. Accessing higher memory regions can trigger memory expansion costs. Solidity uses memory for temporary arrays, ABI encoding, and intermediate values.

Storage

Storage is persistent, contract-specific key-value data. Each slot is conceptually a 256-bit key mapped to a 256-bit value. Solidity assigns storage locations according to deterministic layout rules. State variables, mappings, and dynamic arrays may occupy multiple slots or derive locations by hashing keys and slot positions.

Storage is substantially more expensive than memory because it changes the replicated global state. It should therefore be used deliberately.

Calldata

Calldata is an immutable byte array supplied to a message call. It contains the function selector and ABI-encoded arguments. It is usually cheaper than copying input into memory and is the conventional location for external function parameters.

Code

A contract account has deployed runtime code. Contract creation also uses initialization code, which executes once and returns the runtime bytecode to be stored at the new address. The initialization code itself is not normally the persistent runtime code.

9. ABI Encoding and Function Dispatch

The Application Binary Interface (ABI) defines how contracts encode function calls, arguments, return values, and events. For a standard Solidity function call, calldata begins with a four-byte selector:

selector = first4Bytes(keccak256("functionName(type1,type2,... )"))

The type string uses canonical ABI types without parameter names. The selector is not a complete security mechanism: collisions are possible because four bytes provide only a limited identifier. Contracts must also validate authorization, argument ranges, and state assumptions.

For static ABI values, arguments are placed in 32-byte words. Dynamic values use an offset in the head region pointing to their data in the tail region.

Worked example: a static call

Consider:

function transfer(address to, uint256 amount) external returns (bool);

The calldata structure is:

RegionSizeContent
Function selector4 bytesFirst four bytes of the canonical signature hash
Address argument32 bytesAddress left-padded with zeros
Amount argument32 bytesUnsigned integer encoded as a 32-byte value

The total calldata length is therefore 4 + 32 + 32 = 68 bytes, excluding any transaction-envelope fields. The selector identifies a dispatch branch; the two 32-byte words provide the arguments.

For a dynamic parameter such as bytes, the head contains an offset, while the tail contains a length followed by data padded to a 32-byte boundary. ABI encoding is deterministic, so independently written clients can construct identical calldata.

10. Calls, Delegate Calls, and Context

A normal CALL executes another account’s code with a separate storage context belonging to the called account. DELEGATECALL executes the target code while preserving the caller’s storage context, caller, and call value semantics. This enables proxy contracts but introduces upgradeability and storage-layout risks.

STATICCALL disallows state-changing operations during the call. It is useful for read-only queries, but “read-only” is enforced by EVM execution rules rather than by the Solidity function name alone.

The EVM also exposes contextual values such as:

  • CALLER: immediate caller of the current call frame.
  • ORIGIN: original transaction sender; generally unsuitable for authorization logic.
  • CALLVALUE: ether supplied with the call.
  • ADDRESS: current executing account.
  • NUMBER: block number.
  • TIMESTAMP: block timestamp.
  • CHAINID: network identifier.

Block values are deterministic within a block but are not arbitrary sources of secure randomness. Validators have some bounded influence over certain block fields, and predictable values should not protect high-value lotteries or secrets.

11. Deterministic Execution and Failure Modes

EVM execution is deterministic when all participating nodes use the same:

  • Transaction bytes and ordering.
  • Initial state.
  • Block context.
  • Protocol version and gas schedule.
  • Contract bytecode.

Smart contracts cannot directly perform uncontrolled network requests or read local files. Oracle systems are required for external information, but an oracle introduces an additional trust, governance, and failure surface.

A transaction can fail through an invalid opcode, out-of-gas condition, explicit revert, failed call handling, or invalid transaction-level conditions. A revert generally rolls back state changes made in the reverted call scope, but the transaction’s consumed gas is not automatically restored. Contracts should check return values from low-level calls because some low-level operations return failure indicators instead of automatically propagating errors.

12. Applied Case: A Token Transfer Through Ethereum’s Architecture

The following is an illustrative case.

Background

A user wants to transfer 25 units of an ERC-20 token to another address. The token contract stores balances in a mapping and emits a Transfer event.

Problem

The user’s wallet must create valid calldata, pay sufficient gas, and ensure that the transaction is executed on the intended network. Nodes must agree on the resulting balances and event log.

Analysis

The wallet constructs the canonical function call transfer(address,uint256). It computes the four-byte selector, encodes the recipient as a 32-byte word, and encodes 25 as a 32-byte unsigned integer. The signed transaction identifies the sender, nonce, fee parameters, gas limit, destination contract, and calldata.

The consensus layer places the transaction in an accepted block order. The execution layer invokes the token’s runtime bytecode. The contract checks the sender’s balance, updates two mapping entries, and emits a log. Mapping locations are derived using the key and the mapping’s storage slot according to Solidity’s storage-layout rules. The resulting storage changes alter the contract’s storage-root commitment, which contributes to the global state root.

Approach and outcome

If execution succeeds, the receipt records success, gas used, and the event log. Indexers can interpret the log using the contract’s ABI. If the sender lacks sufficient token balance, the contract reverts; the balance changes are not committed, although gas has been consumed according to the applicable execution path.

Learning points

  1. Wallets, consensus clients, execution clients, contracts, and indexers perform different roles.
  2. ABI encoding connects human-readable function calls to raw calldata.
  3. A token balance is contract storage, not a native account balance.
  4. Event logs assist off-chain systems but are not a replacement for state.
  5. A valid signature does not guarantee successful contract execution.

13. Professional Practice, Governance, and Limitations

Engineering best practices

  • Pin compiler versions and document build settings.
  • Inspect deployed bytecode and verify source-code correspondence where possible.
  • Use established ABI libraries rather than hand-encoding complex dynamic data.
  • Minimize unnecessary storage writes.
  • Check low-level call results and validate return data.
  • Avoid using tx.origin for authorization.
  • Treat proxy storage layout as an upgrade-critical interface.
  • Test across realistic gas conditions and failure paths.
  • Use formal analysis, static analysis, fuzzing, and independent review for valuable contracts.

Governance and regulatory considerations

Ethereum’s execution rules are public, but protocol upgrades can change gas costs, transaction formats, and available opcodes. Production systems require upgrade monitoring and compatibility testing. Applications that issue tokens, custody assets, provide financial services, or process personal data may also face jurisdiction-specific legal obligations.

Blockchain transparency creates a privacy challenge. Addresses are pseudonymous rather than inherently anonymous, and transaction histories can be analyzed. Designers should avoid placing sensitive personal data directly on-chain because deletion and correction are difficult once data is replicated.

Technical limitations

Ethereum execution is replicated, so computation and storage are more costly than on a private centralized server. Block capacity is limited, transaction ordering can create congestion and market-based fees, and smart-contract code is difficult to change after deployment. Layer-2 systems can improve scalability, but they introduce bridge, sequencer, proof, data-availability, and governance considerations.

Contemporary Ethereum development therefore emphasizes modular architecture: the base layer provides settlement and security, while rollups and other systems execute transactions more efficiently and publish data or proofs under defined security assumptions.

Chapter Summary

Ethereum is a replicated state machine composed operationally of a consensus layer and an execution layer. Consensus organizes validators, block proposals, attestations, fork choice, and finality. Execution validates and runs transactions, charges gas, produces receipts and logs, and computes the resulting world state.

The world state maps addresses to account records containing nonce, balance, storage-root, and code-hash information. Merkle Patricia tries provide authenticated commitments to accounts, contract storage, transactions, and receipts. Their roots allow compact verification, but they do not provide confidentiality or independently establish canonical history.

The EVM is a deterministic 256-bit stack machine. It uses transient memory, persistent storage, immutable calldata, bytecode, a program counter, and gas accounting. Solidity compiles into bytecode, while ABI rules encode function selectors, static arguments, dynamic offsets, return values, and events. Determinism depends on shared transaction order, state, block context, and protocol rules. Professional development requires careful handling of gas, storage layout, calls, failures, upgradeability, privacy, and changing protocol specifications.

Key Terms

ABI: Standard encoding interface for contract calls, return values, and events.

Account: An Ethereum state object represented by an address and account fields.

Attestation: A validator’s vote concerning a block or chain checkpoint.

Bytecode: Machine-readable EVM instructions stored in a contract account.

Calldata: Immutable input bytes supplied to a message call.

Consensus layer: Ethereum subsystem coordinating validators, ordering, fork choice, and finality.

Execution layer: Subsystem processing transactions and computing state changes.

EVM: Ethereum Virtual Machine, the deterministic execution environment for smart contracts.

Gas: Unit used to meter computational and state resources.

Merkle Patricia trie: Authenticated, path-organized data structure used for Ethereum commitments.

Nonce: Account counter used for transaction sequencing or contract-creation sequencing.

Opcode: Individual EVM instruction with defined semantics and gas behavior.

Receipt: Post-execution record containing status, gas information, and logs.

Runtime bytecode: Contract code stored and executed after deployment.

Selector: Four-byte identifier derived from a canonical function signature.

Smart contract: An account containing EVM code and, commonly, persistent storage.

State root: Cryptographic commitment to the global world state.

Storage: Persistent contract-specific key-value data.

World state: The current mapping from Ethereum addresses to account records.

Review Questions

Short Answer Questions

  1. What is the primary difference between Ethereum’s consensus layer and execution layer?
  2. Name the four conceptual fields in an Ethereum account record.
  3. Why is persistent storage more costly than memory in the EVM?
  4. What information does a function selector identify?
  5. What is the purpose of gas in Ethereum execution?

Long Answer Questions

  1. Explain Ethereum’s state-transition model and describe the main stages of transaction processing.
  2. Compare the EVM stack, memory, storage, calldata, and runtime code.
  3. Explain how Merkle Patricia tries provide authenticated commitments to Ethereum state.
  4. Describe standard ABI encoding for a function with static and dynamic arguments.
  5. Explain how the consensus and execution layers cooperate to accept a block.

Analytical / Application Questions

  1. A contract call succeeds on one test but fails after deployment because the transaction supplies too little gas. Analyze the likely causes and identify how gas estimation and failure handling should be investigated.
  2. A proxy contract is upgraded and users report that balances appear corrupted. Analyze how DELEGATECALL, storage-layout incompatibility, and upgrade governance could produce this outcome.
  3. Design the processing path for an ERC-20 transfer from wallet creation through consensus inclusion, EVM execution, state-root update, receipt generation, and off-chain event indexing.

Multiple Choice Questions with Answers

Question 1

Which layer primarily executes transactions and computes the resulting Ethereum state?

A. Consensus layer

B. Execution layer

C. Peer-discovery layer

D. Wallet interface layer

Correct Answer: B. Execution layer

Question 2

Which EVM component provides persistent contract-specific key-value data across transactions?

A. Memory

B. Calldata

C. Storage

D. Return data

Correct Answer: C. Storage

Question 3

What is the main purpose of a state root?

A. To encrypt all account balances

B. To identify a private key

C. To commit cryptographically to the world state

D. To determine the validator’s identity

Correct Answer: C. To commit cryptographically to the world state

Question 4

What is the standard size of an EVM stack word?

A. 4 bytes

B. 16 bytes

C. 32 bytes

D. 64 bytes

Correct Answer: C. 32 bytes

Question 5

For a standard ABI function call, what does the first four bytes of calldata normally represent?

A. The sender’s balance

B. The function selector

C. The block hash

D. The gas refund

Correct Answer: B. The function selector

Question 6

What happens to ordinary state changes made inside a reverted call?

A. They are committed twice

B. They are rolled back

C. They are moved to calldata

D. They become validator attestations

Correct Answer: B. They are rolled back

Question 7

Which operation preserves the caller’s storage context while executing another account’s code?

A. CALL

B. STATICCALL

C. DELEGATECALL

D. CREATE

Correct Answer: C. DELEGATECALL

Question 8

A standard ABI call has a 4-byte selector and two static arguments of 32 bytes each. Excluding the transaction envelope, what is its calldata length?

A. 36 bytes

B. 64 bytes

C. 68 bytes

D. 96 bytes

Correct Answer: C. 68 bytes

Question 9

Why can a contract not safely use an arbitrary external website as a direct source of execution data?

A. The EVM has no deterministic bytecode

B. Uncontrolled external requests would undermine deterministic execution

C. Contracts cannot contain storage

D. Validators cannot process calldata

Correct Answer: B. Uncontrolled external requests would undermine deterministic execution

Question 10

Which statement best describes Ethereum gas?

A. A cryptographic signature format

B. A unit for metering computational and state resources

C. A type of persistent storage slot

D. A validator’s private key

Correct Answer: B. A unit for metering computational and state resources

Ethereum & Solidity
Chapter 2 of 15