EVM in Action, Part 1: Introduction

Posted June 3, 2024 by mjzk ‐ 4 min read

The Ethereum Virtual Machine (EVM) is the beating heart of the Ethereum blockchain, enabling the execution of smart contracts and decentralized applications (dApps). Understanding the EVM is crucial for anyone interested in Ethereum development. In this first part of our blog series, we'll introduce the EVM, explore its memory model, execution model, gas model, common opcodes, and the translation from Solidity to opcodes.

What is the EVM?

The EVM is a decentralized computer that executes smart contracts in a deterministic and isolated environment. It's a virtual machine designed to run bytecode instructions, ensuring that the same input will always produce the same output, regardless of where the execution takes place. This feature is essential for maintaining consensus across the Ethereum network.

The Memory Model

The EVM uses three types of storage:

  1. Storage: Persistent storage that is part of the blockchain state. Each contract has its own storage, which is a key-value store accessible via SSTORE and SLOAD opcodes. Storage is expensive in terms of gas costs due to its permanence.

  2. Memory: Volatile storage that is reset after the execution of each transaction. Memory is cheaper than storage and is accessed via MSTORE and MLOAD opcodes. It's used for intermediate calculations and data manipulation within a transaction.

  3. Stack: A last-in-first-out (LIFO) data structure used for holding small data and performing operations. The stack is extremely fast and is accessed via opcodes like PUSH, POP, DUP, and SWAP.

Execution Model

The EVM operates in a sequential manner, executing one instruction at a time. Each operation depends on the previous one, creating a predictable execution flow. Although there have been discussions about parallel execution to improve performance, the current EVM design focuses on sequential execution to maintain simplicity and determinism.

Gas Model

Gas is a fundamental concept in the EVM, serving as a measure of computational effort required to execute operations. Every EVM instruction costs a certain amount of gas, which is deducted from the transaction's gas limit. The gas model serves several purposes:

  • Prevents infinite loops: By requiring gas for every operation, the EVM ensures that contracts will eventually halt.
  • Mitigates denial-of-service attacks: High gas costs discourage malicious actors from spamming the network with complex transactions.
  • Prioritizes transactions: Miners are incentivized to include transactions with higher gas fees in blocks.

Common gas-related opcodes include GAS, which returns the remaining gas, and GASPRICE, which returns the gas price set by the transaction.

Common Opcodes

EVM opcodes are low-level instructions that perform various tasks. Here are some of the most commonly used ones:

  • PUSH (0x60-0x7f): Pushes a value onto the stack.
  • POP (0x50): Removes the top value from the stack.
  • ADD (0x01): Adds the top two values on the stack.
  • MUL (0x02): Multiplies the top two values on the stack.
  • SSTORE (0x55): Stores a value in contract storage.
  • SLOAD (0x54): Loads a value from contract storage.
  • JUMP (0x56) and JUMPI (0x57): Alters the flow of execution.

From Solidity to Opcodes

Solidity is a high-level programming language for writing smart contracts on Ethereum. When a Solidity contract is compiled, it is translated into EVM bytecode, a sequence of opcodes. This process involves several steps:

  1. Parsing and syntax checking: The Solidity code is parsed, and its syntax is checked for errors.
  2. Abstract syntax tree (AST) generation: The code is transformed into an AST, representing the structure of the program.
  3. Intermediate representation (IR): The AST is converted into an IR, which is easier to optimize and analyze.
  4. Bytecode generation: The IR is translated into EVM bytecode, consisting of opcodes that the EVM can execute.

For example, a simple Solidity function like function add(uint a, uint b) public pure returns (uint) { return a + b; } would be translated into a series of opcodes that push the values of a and b onto the stack, perform the addition, and return the result.

Conclusion

Understanding the EVM is essential for developing efficient and secure smart contracts. In this introduction, we've covered the basics of the EVM, including its memory model, execution model, gas model, common opcodes, and the translation from Solidity to opcodes. In the next part of our series, we'll dive deeper into the intricacies of EVM opcodes and their practical applications.

Stay tuned for Part 2 of "EVM in Action"!