DEV Community

Cover image for The Ethereum Virtual Machine: Fueling the Decentralized Revolution
Sahil Sojitra
Sahil Sojitra

Posted on

The Ethereum Virtual Machine: Fueling the Decentralized Revolution

The Ethereum Virtual Machine (EVM) is at the core of the Ethereum protocol. It acts as a powerful computation engine, somewhat similar to virtual machines used in technologies like Microsoft's .NET Framework or interpreters for programming languages like Java. In this chapter, we'll delve into the EVM, understanding its instruction set, structure, and how it operates within Ethereum's state updates.

Imagine the EVM as a virtual computer that exists within the Ethereum network. It carries out instructions and performs calculations, ensuring that transactions and contracts run as intended. This virtual machine plays a crucial role in maintaining trust and decentralization in the Ethereum ecosystem.

By grasping the fundamentals of the EVM, you'll gain insights into how Ethereum processes transactions, keeps track of its state, and enables the execution of smart contracts. Join us on an exciting exploration of decentralized computing and the technology driving Ethereum forward.

What is the EVM?

The Ethereum Virtual Machine (EVM) is responsible for handling smart contract deployment and execution within the Ethereum network. While simple transactions involving value transfers don't require the EVM, any other operation will involve the EVM computing a state update.

Think of the EVM as a giant decentralized computer on the Ethereum blockchain, consisting of numerous executable objects. Each object has its own permanent data store, making the EVM a global computational system.

The EVM operates as a quasi-Turing-complete state machine, meaning it can perform complex computations but with a limitation. The execution of smart contracts is restricted by the available gas, which determines the number of computational steps allowed. This ensures that all program executions will eventually halt, preventing any potential situation where the Ethereum platform comes to a complete halt due to endless execution.

The EVM uses a stack-based architecture, where in-memory values are stored on a stack. It employs a word size of 256 bits, primarily for efficient hashing and elliptic curve operations. The EVM comprises different components:

  1. Immutable program code (ROM): It stores the bytecode of the smart contract being executed.
  2. Volatile memory: This memory is initialized to zero, and its values are temporary during the execution.
  3. Permanent storage: Part of the Ethereum state, this storage is also initialized to zero and retains values even after the execution ends.

Additionally, the EVM has a set of environment variables and data available during execution, which we will explore in more detail later in this chapter.

evm-architecture

Comparison with Existing Technology

The term "virtual machine" is often used to describe simulating real computers or operating systems. However, the Ethereum Virtual Machine (EVM) is different. It acts as a computation engine, focused on handling calculations and storage. It's similar to the Java Virtual Machine (JVM) used for languages like Java or Scala.

The EVM executes special instructions, called bytecode, that are specific to Ethereum. Higher-level smart contract programming languages like LLL, Serpent, Mutan, or Solidity are converted into this bytecode. Just like the JVM runs Java bytecode, the EVM runs Ethereum's bytecode.

Unlike regular virtual machines, the EVM doesn't manage scheduling or interact with physical hardware. Ethereum clients handle the order of smart contracts to execute, based on verified transactions. Think of the Ethereum world computer as running one task at a time, similar to how JavaScript works. It operates virtually without needing any physical hardware to connect to.

The EVM Instruction Set (Bytecode Operations)

The EVM instruction set includes different operations that allow you to perform calculations, access information about the execution context, manipulate data in the stack, memory, and storage, control the flow of the program, and interact with other contracts.

Here are some key points about the EVM instruction set:

  • Arithmetic and bitwise logic operations: You can use these operations to perform basic calculations like adding, subtracting, multiplying, dividing, and finding remainders. You can also perform logical operations like AND, OR, XOR, and NOT.
  • Execution context inquiries: The EVM lets you ask questions about the current execution context, such as getting the address and balance of an account, finding the block number, or knowing the current gas price.
  • Stack, memory, and storage access: You can use instructions to manage a stack, which is a temporary storage for data. You can read from and write to memory, which is like a temporary working area. Additionally, you can read from and write to storage, which is a persistent storage associated with each contract.
  • Control flow operations: The EVM allows you to control the flow of the program by making decisions based on conditions, jumping to different parts of the code, and stopping execution if needed.
  • Logging, calling, and other operators: You can use instructions to log events, make calls to other contracts, self-destruct a contract, or handle exceptions like reverting changes.

The EVM also provides access to information about accounts, such as their addresses and balances, as well as block-related details like the block number and gas price.

The available opcodes can be divided into following categories:

Arithmetic Operations:

ADD        //Add the top two stack items
MUL        //Multiply the top two stack items
SUB        //Subtract the top two stack items
DIV        //Integer division
SDIV       //Signed integer division
MOD        //Modulo (remainder) operation
SMOD       //Signed modulo operation
ADDMOD     //Addition modulo any number
MULMOD     //Multiplication modulo any number
EXP        //Exponential operation
SIGNEXTEND //Extend the length of a two's complement signed integer
SHA3       //Compute the Keccak-256 hash of a block of memory
Enter fullscreen mode Exit fullscreen mode

Stack Operations:

POP     //Remove the top item from the stack
MLOAD   //Load a word from memory
MSTORE  //Save a word to memory
MSTORE8 //Save a byte to memory
SLOAD   //Load a word from storage
SSTORE  //Save a word to storage
MSIZE   //Get the size of the active memory in bytes
PUSHx   //Place x byte item on the stack, where x can be any integer from
        // 1 to 32 (full word) inclusive
DUPx    //Duplicate the x-th stack item, where x can be any integer from
        // 1 to 16 inclusive
SWAPx   //Exchange 1st and (x+1)-th stack items, where x can be any
        // integer from 1 to 16 inclusive
Enter fullscreen mode Exit fullscreen mode

Process Flow Operations:

STOP      //Halt execution
JUMP      //Set the program counter to any value
JUMPI     //Conditionally alter the program counter
PC        //Get the value of the program counter (prior to the increment
          //corresponding to this instruction)
JUMPDEST  //Mark a valid destination for jumps
Enter fullscreen mode Exit fullscreen mode

System Operations:

LOGx          //Append a log record with x topics, where x is any integer
              //from 0 to 4 inclusive
CREATE        //Create a new account with associated code
CALL          //Message-call into another account, i.e. run another
              //account's code
CALLCODE      //Message-call into this account with another
              //account's code
RETURN        //Halt execution and return output data
DELEGATECALL  //Message-call into this account with an alternative
              //account's code, but persisting the current values for
              //sender and value
STATICCALL    //Static message-call into an account
REVERT        //Halt execution, reverting state changes but returning
              //data and remaining gas
INVALID       //The designated invalid instruction
SELFDESTRUCT  //Halt execution and register account for deletion
Enter fullscreen mode Exit fullscreen mode

Logic Operations:

LT     //Less-than comparison
GT     //Greater-than comparison
SLT    //Signed less-than comparison
SGT    //Signed greater-than comparison
EQ     //Equality comparison
ISZERO //Simple NOT operator
AND    //Bitwise AND operation
OR     //Bitwise OR operation
XOR    //Bitwise XOR operation
NOT    //Bitwise NOT operation
BYTE   //Retrieve a single byte from a full-width 256-bit word
Enter fullscreen mode Exit fullscreen mode

Environmental Operations:

GAS            //Get the amount of available gas (after the reduction for
               //this instruction)
ADDRESS        //Get the address of the currently executing account
BALANCE        //Get the account balance of any given account
ORIGIN         //Get the address of the EOA that initiated this EVM
               //execution
CALLER         //Get the address of the caller immediately responsible
               //for this execution
CALLVALUE      //Get the ether amount deposited by the caller responsible
               //for this execution
CALLDATALOAD   //Get the input data sent by the caller responsible for
               //this execution
CALLDATASIZE   //Get the size of the input data
CALLDATACOPY   //Copy the input data to memory
CODESIZE       //Get the size of code running in the current environment
CODECOPY       //Copy the code running in the current environment to
               //memory
GASPRICE       //Get the gas price specified by the originating
               //transaction
EXTCODESIZE    //Get the size of any account's code
EXTCODECOPY    //Copy any account's code to memory
RETURNDATASIZE //Get the size of the output data from the previous call
               //in the current environment
RETURNDATACOPY //Copy data output from the previous call to memory
Enter fullscreen mode Exit fullscreen mode

Block Operations:

BLOCKHASH  //Get the hash of one of the 256 most recently completed
           //blocks
COINBASE   //Get the block's beneficiary address for the block reward
TIMESTAMP  //Get the block's timestamp
NUMBER     //Get the block's number
DIFFICULTY //Get the block's difficulty
GASLIMIT   //Get the block's gas limit
Enter fullscreen mode Exit fullscreen mode

Ethereum State

The Ethereum Virtual Machine (EVM) is responsible for updating the Ethereum network's records as a result of executing smart contracts. This process is like a chain reaction, where transactions initiated by account holders and miners trigger changes in the overall state of the network.

At the highest level, we have the Ethereum world state, which is a mapping of Ethereum addresses to accounts. Each Ethereum address represents an account and includes information such as the account's ether balance (measured in wei), a nonce (which tracks successful transactions or created contracts), storage (used by smart contracts), and program code (for contract accounts). An externally owned account (EOA) has no code and empty storage.

When a transaction involves executing smart contract code, the EVM is set up with all the necessary information. This includes loading the code of the contract being called, initializing the program counter, loading the storage of the contract account, setting memory to zero, and setting the block and environment variables. The gas supply for the execution is also set based on the amount of gas paid by the sender. As code execution progresses, the gas supply is reduced according to the gas cost of the operations performed. If the gas supply is depleted, an "Out of Gas" exception occurs, and execution stops without applying any changes to the Ethereum state, except for incrementing the sender's nonce and deducting their ether balance. The EVM operates on a sandboxed copy of the Ethereum world state, and if execution is successful, the real-world state is updated to match the sandboxed version.

It's important to note that smart contracts can initiate further transactions, leading to a recursive process. A contract can call other contracts, and each call results in a new EVM being instantiated for the target contract. The sandbox world state of each instantiation is initialized from the sandbox of the EVM above it. Each instantiation is given a specific amount of gas, and if it runs out of gas, the sandbox state is discarded, and execution returns to the EVM above.

In simpler terms, the EVM's job is to update the Ethereum state based on the execution of smart contracts. It does this by following a set of rules and maintaining a sandboxed version of the state. Transactions trigger code execution, and if it completes successfully, the real-world state is updated accordingly. Recursive calls can occur, creating new sandboxes for each contract called, and gas limits ensure that execution doesn't continue indefinitely.

Turing Completeness and Gas

In simpler terms, being Turing complete means that a system or programming language can run any program. However, there is a problem called the halting problem, which means we can't know in advance if a program will run forever or actually finish executing.

This poses a risk for Ethereum because it operates as a single-threaded machine without a scheduler. If a program gets stuck in an infinite loop, Ethereum would become unusable.

To address this, Ethereum introduced the concept of gas. Gas provides a solution by setting a maximum amount of computation that a program can perform. If the execution of a program exceeds this limit, the Ethereum Virtual Machine (EVM) halts the execution. This means that the EVM is "quasi-Turing complete" because it can run any program as long as it terminates within a specific amount of computation.

The gas limit is not fixed in Ethereum. Users have the option to pay more to increase the limit, up to a maximum called the "block gas limit." Over time, this limit can be increased by consensus. However, at any given time, there is a limit in place, and transactions that consume too much gas during execution are stopped from running indefinitely.

In summary, gas provides a way to prevent programs from running forever and causing Ethereum to become unresponsive. It sets a maximum limit on computation, ensuring that programs have a predefined amount of resources to execute within.

Gas

In simple terms, gas is a unit of measurement in Ethereum that determines the computational and storage resources needed for actions on the Ethereum blockchain. Unlike Bitcoin, where transaction fees are based on transaction size, Ethereum considers the computational steps taken by transactions and smart contract code.

Every action in Ethereum has a fixed gas cost associated with it. Here are a few examples:

  • Adding two numbers costs 3 gas.
  • Calculating a Keccak-256 hash costs 30 gas, plus 6 gas for each 256 bits of data being hashed.
  • Sending a transaction costs 21,000 gas.

Gas plays two important roles in Ethereum. First, it acts as a buffer between the price of Ethereum and the rewards given to miners for their work. Second, it serves as a defense against denial-of-service attacks. To prevent infinite loops or wasteful computations, the sender of a transaction needs to set a limit on the amount of computation they are willing to pay for.

The gas system discourages attackers from sending spam transactions because they have to pay for the resources they consume. This includes computational power, bandwidth, and storage. By requiring a payment for these resources, Ethereum disincentivizes attackers from flooding the network with unnecessary or malicious transactions.

Gas Accounting During Execution

When an Ethereum Virtual Machine (EVM) executes a transaction, it is given a specific amount of gas determined by the gas limit in the transaction. Each opcode (operation) executed by the EVM has a gas cost, which reduces the available gas as the program is executed. Before each operation, the EVM checks if there is enough gas to cover its cost. If there isn't enough gas, the execution stops, and the transaction is reversed.

If the EVM successfully completes the execution without running out of gas, the gas cost used is paid to the miner as a transaction fee. The fee is calculated by multiplying the gas cost by the gas price specified in the transaction. Additionally, any remaining gas in the gas supply is refunded to the sender. The refunded gas is converted to ether based on the gas price.

miner fee = gas cost * gas price
Enter fullscreen mode Exit fullscreen mode

However, if the transaction runs out of gas during execution, it is immediately terminated, triggering an "out of gas" exception. The transaction is reverted, and any changes made to the state are rolled back.

remaining gas = gas limit - gas cost
refunded ether = remaining gas * gas price
Enter fullscreen mode Exit fullscreen mode

Despite the unsuccessful transaction, the sender is still charged a transaction fee because the miners have already performed computational work up to that point and need to be compensated for their efforts.

Gas Accounting Considerations

Gas costs for different operations in the Ethereum Virtual Machine (EVM) have been carefully selected to protect the Ethereum blockchain from potential attacks. These costs ensure that more computationally intensive operations require more gas.

For example, executing the SHA3 function costs 10 times more gas (30 gas) than the ADD operation (3 gas). Certain operations, like EXP, have an additional payment based on the size of the operand. Gas costs are also incurred when using EVM memory or storing data in a contract's on-chain storage.

The importance of aligning gas costs with real-world resource costs was demonstrated in 2016. An attacker exploited a discrepancy in gas costs, creating transactions that were extremely computationally expensive. This led to a severe slowdown of the Ethereum mainnet. To address this issue, a hard fork called "Tangerine Whistle" was implemented, adjusting the relative gas costs of operations.

Gas Cost Versus Gas Price

When you perform a transaction on the Ethereum network, there are two important things to consider: gas cost and gas price.

Gas cost: This is the amount of computational and storage resources needed to complete an operation on the Ethereum Virtual Machine (EVM). Different operations have different gas costs. More complex operations require more gas, while simpler operations require less.

Gas price: This is the price you are willing to pay, in ether, for each unit of gas used in your transaction. It represents the value of the computational resources you are using. You set the gas price when you send your transaction to the network.

Transaction fee: The transaction fee is the total cost of your transaction and is calculated by multiplying the gas used by the gas price. It represents the payment you make to miners for processing and confirming your transaction. Miners are more likely to include transactions with higher gas prices because it increases their potential earnings.

Gas limit: When you send a transaction, you also set a gas limit, which is the maximum amount of gas you are willing to use. It should be set higher than or equal to the expected gas usage of your transaction. If the actual gas used is less than the gas limit, you will receive a refund of the excess gas.

Although gas has a price, it cannot be possessed or used as a form of currency. Gas is simply a measurement of computational work within the Ethereum Virtual Machine (EVM). When a transaction is sent, the sender is charged a transaction fee in ether. This fee is then converted into gas for accounting purposes within the EVM. Finally, the gas fee is converted back into ether as a payment to the miners for their work in confirming and processing the transaction.

Negative Gas Costs

Ethereum rewards users for cleaning up storage and accounts by giving them refunds for the gas used during contract execution.

There are two actions in Ethereum that actually save gas costs:

  • When a contract is deleted (SELFDESTRUCT), the user receives a refund of 24,000 gas.
  • Changing a storage address from a non-zero value to zero (SSTORE[x] = 0) results in a refund of 15,000 gas.

To prevent abuse of the refund system, there is a maximum limit on the refund amount for each transaction. This limit is set to half of the total gas used in the transaction, rounded down to the nearest whole number. This ensures that refunds are fair and don't become excessive.

In simple terms, Ethereum encourages users to remove unnecessary storage and accounts by offering refunds for certain actions. Deleting a contract or setting a storage address to zero results in gas refunds. However, there is a maximum refund limit to maintain fairness and prevent misuse of the system.

Block Gas Limit

The block gas limit is the maximum amount of gas that can be used by all the transactions within a block. It determines how many transactions can fit into a block.

For example, let's say we have 5 transactions with gas limits set to 30,000, 30,000, 40,000, 50,000, and 50,000. If the block gas limit is 180,000, only four of these transactions can fit into a block, and the fifth one will have to wait for the next block. Miners decide which transactions to include in a block, and different miners may choose different combinations of transactions because they receive them in different orders from the network.

If a miner tries to include a transaction that requires more gas than the current block gas limit, the network will reject the block. Ethereum clients usually warn users with messages like "transaction exceeds block gas limit" to prevent such transactions from being issued.

On the Ethereum mainnet, the current block gas limit is around 8 million gas (according to etherscan.io at the time of writing). This means approximately 380 basic transactions, each consuming 21,000 gas, can fit into a block.

Who Decides What the block gas limit is?

Ethereum miners decide the block gas limit by voting. They use mining software to connect to Ethereum clients. The gas limit can be adjusted by a small percentage, around 0.0976%, in either direction. This allows the network's block size to be flexible based on miner decisions. The default mining strategy aims for a gas limit of at least 4.7 million gas, but it also considers the average gas usage per block using a moving average calculation over 1,024 blocks. Miners play a crucial role in determining the block gas limit and adapting the network's capacity.

Conclusions

we have delved into the Ethereum Virtual Machine (EVM) and examined how it operates when executing different smart contracts. We have observed the step-by-step execution of bytecode within the EVM. Furthermore, we have explored the concept of gas, which serves as the accounting mechanism for the EVM. Gas effectively addresses the halting problem and acts as a defense against denial-of-service attacks in the Ethereum network.

Top comments (2)

Collapse
 
bhavypatel45 profile image
Bhavypatel45

This blog post about the Ethereum Virtual Machine (EVM) is really informative! It explains how the EVM powers the decentralized revolution. I learned that the EVM is like a computer that runs smart contracts on the Ethereum blockchain. It's amazing how the EVM enables developers to create and execute decentralized applications (DApps) with reliability and security. The article did a great job of breaking down the technical aspects of the EVM into understandable terms. I now have a better understanding of how the EVM plays a crucial role in the Ethereum ecosystem. Thanks for sharing this valuable information!

Collapse
 
utsavdesai26 profile image
Utsav Desai

An enlightening journey ahead! This article appears poised to delve into the Ethereum Virtual Machine's role in driving the decentralized revolution. Excited to learn how this virtual powerhouse empowers smart contracts and fuels the transformation of various industries.