Secureum Book
  • 🛡️Secureum Bootcamp
    • 🛡️Secureum Bootcamp
    • 🙌Participate
    • 📜History
  • 📚LEARN
    • Introduction
      • 🔷1. Ethereum Basics
        • 1.1 Ethereum: Concept, Infrastructure & Purpose
        • 1.2 Properties of the Ethereum Infrastructure
        • 1.3 Ethereum vs. Bitcoin
        • 1.4 Ethereum Core Components
        • 1.5 Gas Metering: Solving the Halting Problem
        • 1.6 web2 vs. web3: The Paradigm Shift
        • 1.7 Decentralization
        • 1.8 Cryptography, Digital Signature & Keys
        • 1.9 Ethereum State & Account Types
        • 1.10 Transactions: Properties & Components
        • 1.11 Contract Creation
        • 1.12 Transactions, Messages & Blockchain
        • 1.13 EVM (Ethereum Virtual Machine) in Depth
        • 1.14 Transaction Reverts & Data
        • 1.15 Block Explorer
        • 1.16 Mainnet & Testnets
        • 1.17 ERCs & EIPs
        • 1.18 Legal Aspects in web3: Pseudonymity & DAOs
        • 1.19 Security in web3
        • 1.20 web2 Timescales vs. web3 Timescales
        • 1.21 Test-in-Prod. SSLDC vs. Audits
        • Summary: 101 Keypoints
      • 🌀2. Solidity
        • 2.1 Solidity: Influence, Features & Layout
        • 2.2 SPDX & Pragmas
        • 2.3 Imports
        • 2.4 Comments & NatSpec
        • 2.5 Smart Contracts
        • 2.6 State Variables: Definition, Visibility & Mutability
        • 2.7 Data Location
        • 2.8 Functions
        • 2.9 Events
        • 2.10 Solidity Typing
        • 2.11 Solidity Variables
        • 2.12 Address Type
        • 2.13 Conversions
        • 2.14 Keywords & Shorthand Operators
        • 2.15 Solidity Units
        • 2.16 Block & Transaction Properties
        • 2.17 ABI Encoding & Decoding
        • 2.18 Error Handling
        • 2.19 Mathematical & Cryptographic Functions
        • 2.20 Control Structures
        • 2.21 Style & Conventions
        • 2.22 Inheritance
        • 2.23 EVM Storage
        • 2.24 EVM Memory
        • 2.25 Inline Assembly
        • 2.26 Solidity Version Changes
        • 2.27 Security Checks
        • 2.28 OpenZeppelin Libraries
        • 2.29 DAppSys Libraries
        • 2.30 Important Protocols
        • Summary: 201 Keypoints
      • 🔏3. Security Pitfalls & Best Practices
        • 3.1 Solidity Versions
        • 3.2 Access Control
        • 3.3 Modifiers
        • 3.4 Constructor
        • 3.5 Delegatecall
        • 3.6 Reentrancy
        • 3.7 Private Data
        • 3.8 PRNG & Time
        • 3.9 Math & Logic
        • 3.10 Transaction Order Dependence
        • 3.11 ecrecover
        • 3.12 Unexpected Returns
        • 3.13 Ether Accounting
        • 3.14 Transaction Checks
        • 3.15 Delete Mappings
        • 3.16 State Modification
        • 3.17 Shadowing & Pre-declaration
        • 3.18 Gas & Costs
        • 3.19 Events
        • 3.20 Unary Expressions
        • 3.21 Addresses
        • 3.22 Assertions
        • 3.23 Keywords
        • 3.24 Visibility
        • 3.25 Inheritance
        • 3.26 Reference Parameters
        • 3.27 Arbitrary Jumps
        • 3.28 Hash Collisions & Byte Level Issues
        • 3.29 Unicode RTLO
        • 3.30 Variables
        • 3.31 Pointers
        • 3.32 Out-of-range Enum
        • 3.33 Dead Code & Redundant Statements
        • 3.34 Compiler Bugs
        • 3.35 Proxy Pitfalls
        • 3.36 Token Pitfalls
        • 3.37 Special Token Pitfalls
        • 3.38 Guarded Launch Pitfalls
        • 3.39 System Pitfalls
        • 3.40 Access Control Pitfalls
        • 3.41 Testing, Unused & Redundand Code
        • 3.42 Handling Ether
        • 3.43 Application Logic Pitfalls
        • 3.44 Saltzer & Schroeder's Design Principles
        • Summary: 201 Keypoints
      • 🗜️4. Audit Techniques & Tools
        • 4.1 Audit
        • 4.2 Analysis Techniques
        • 4.3 Specification, Documentation & Testing
        • 4.4 False Positives & Negatives
        • 4.5 Security Tools
        • 4.6 Audit Process
        • Summary: 101 Keypoints
      • ☝️5. Audit Findings
        • 5.1 Criticals
        • 5.2 Highs
        • 5.3 Mediums
        • 5.4 Lows
        • 5.5 Informationals
        • Summary: 201 Keypoints
  • 🌱CARE
    • CARE
      • CARE Reports
  • 🚩CTFs
    • A-MAZE-X CTFs
      • Secureum A-MAZE-X
      • Secureum A-MAZE-X Stanford
      • Secureum A-MAZE-X Maison de la Chimie Paris
Powered by GitBook
On this page
  • EVM Arquitecture
  • EVM Ordering
  • Instruction Set
  • Gas Costs
  1. LEARN
  2. Introduction
  3. 1. Ethereum Basics

1.13 EVM (Ethereum Virtual Machine) in Depth

The EVM is the execution component of the Ethereum blockchain: it is the runtime environment where all the smart contracts run. Recall that EVM is a quasi-Turing complete machine: it's turing complete because the underlying programming language supports arbitrary logic unbounded complexity, but it's also bounded by the amount of Gas provided as part of every transaction.

The Ethereum code runs within the EVM and it is written in a low level stack based language referred to as the EVM Machine Code. This code consists of a series of bytes (therefore referred to as a bytecode) where every byte represents a single operation. So the opcodes are very simple and each of them is a single byte.

EVM Arquitecture

Computer architectures are typically classified into either von Neumann architecture or Harvard architecture. This depends on how code and data are handled within the architecture: Are they stored together? Are they transported over the buses together? How are they cached? And so on...

In the case of the EVM, the code is stored separately in a virtual ROM and there is a special instruction to access the EVM code.

EVM is a very simple stack based architecture: the operands for EVM instructions are placed on the stack and the output of those instructions is also returned on the stack. There's no concept of registers, virtual registers or anything like that.

Every architecture has a concept of a word and in the case of the EVM, the word size is 256 bits. It's believed that this was chosen to facilitate some of the fundamental operations around the 256 hash scheme and the elliptic curve computations.

The architecture is made up of four fundamental components:

  1. The stack The EVM has 1024 elements in the stack and each of those elements is 256 bits in length (equal to the word size). EVM instructions are allowed to operate with the top 16 stack elements. Most EVM instructions operate with the stack (because it's a stack based architecture) and there are also stack specific operations.

  2. The volatile memory: in EVM, data placed in memory is not persistent across transactions on the blockchain. It is also linear (it's a byte array and therefore addressable at byte level) and zero initialized.\

    There are three specific instructions that operate with memory, such as MLOAD which loads a word from memory and puts it onto the stack; MSTORE which stores a word in memory from the stack; and MSTORE8 which stores a single byte in memory from the stack. These instructions (and more) will be reviewed in more detail in the following sections.

  3. The non-volatile storage. Unlike memory, storage in EVM is non-volatile: data put in storage is persistent across transactions on the blockchain. It is implemented as a (key, value) store between 256 bit keys and 256 bit values, and it is also zero initialized.\

    To understand how storage fits in within the concept of accounts and the blocks on the blockchain, recall that every account has a storageRoot field. This storageRoot field, implemented as a modified Merkle-Patricia tree, captures all the storage associated with that account. This is relevant for contract accounts that have associated storage. These storageRoots within the account are further captured as part of the stateRoot, which is one of the fields in the block header.\

    There are two instructions that operate specifically on storage: SLOAD which loads a word from the storage and puts it onto the stack; and SSTORE which takes a word from the stack and puts it into storage.

  4. Calldata: it is used specifically for data parameters of transactions and message calls. It is read only (it cannot be written to) and it's also bite addressable.\

    There are three specific instructions that operate with call data: CALLDATASIZE which gives the size of the supplied call data and puts it onto the stack; CALLDATALOAD which loads the call data supplied onto the stack; and CALLDATACOPY that copies the supply call data to specific region of memory.

EVM Ordering

Another concept typically associated with architectures is the concept of ordering: big-endian ordering versus little-endian ordering. In the case of the EVM, it uses the big-endian ordering: the most significant byte of a word is stored at the smallest memory address while the least significant byte is stored at the largest address.

Instruction Set

Summarized walkthrough of the EVM opcodes

All the instructions supported by the EVM can be classified into 11 categories. Instructions that are found in categories a to i operate on the stack.

The format for each of these instructions will be as follows:

OPCODE MNEMONIC INPUTS OUTPUTS

Let's see an example:\

The opcode is the hex representation of the instruction. You will see that the 0x00 opcode is used for the stop instruction. In addition, the word STOP is the mnemonic of the instruction. and then the two numbers that you see after the mnemonic refer to the number of stack items placed for this instruction (inputs) and the number of stack items removed (outputs).

So the stop opcode is 0x00, thus it's the first instruction in the instruction set mapping. The mnemonic is STOP (makes sense), 0 items are placed and 0 items are removed from the stack.

In the case of ADD, you will see that it has 2 items placed onto the stack (the 2 operands) and the computed result (the addition) is placed back onto the stack.

That's why you see that there is one item placed onto the stack, which is the result the addition of the two inputs. The same thing holds good for multiplication, and so on...

a. Stop & Arithmetic

0x00 STOP 0 0
0x01 ADD 2 1
0x02 MUL 2 1
0x03 SUB 2 1
0x04 DIV 2 1
0x05 SDIV 2 1
0x06 MOD 2 1
0x07 SMOD 2 1
0x08 ADDMOD 3 1
0x06 MOD 2 1
0x07 SMOD 2 1
0x08 ADDMOD 3 1
0x09 MULMOD 3 1
0x0a EXP 2 1
0x0b SIGNEXTEND 2 1

b. Comparison & Bitwise Logic

0x10 LT 2 1
0x12 SLT 2 1
0x20 GT 2 1
0x13 SGT 2 1
0x14 EQ 2 1
0x15 ISZERO 1 1
0x16 AND 2 1
0x17 OR 2 1
0x18 XOR 2 1
0x19 NOT 1 1
0x1a BYTE 2 1   
0x1b SHL 2 1
0x1c SHR 2 1
0x1d SAR 2 1

c. SHA3 Instruction

0x20 SHA3 2 1

This single instruction is critical to Ethereum because it computes the Keccak-256 Hash. The formal notation for how the Keccak-256 hash is calculated is:

μs′[0]=KEC(μm[μs[0](μs[0]+μs[1]−1)])μi′=M(μi,μs[0],μs[1])\mu_s'[0]=\text{KEC}\left(\mu_m\left[\mu_s[0]\left(\mu_s[0]+\mu_s[1]-1\right)\right]\right)\\ \mu_i'=\text{M}\left(\mu_i,\mu_s[0],\mu_s[1]\right)μs′​[0]=KEC(μm​[μs​[0](μs​[0]+μs​[1]−1)])μi′​=M(μi​,μs​[0],μs​[1])

This is explained with more detail in the Ethereum Yellowpaper.

d. Environmental Information Instructions

These set of instructions give information about the environment or the execution context of the smart contract executing them.

0x30 ADDRESS 0 1
0x31 BALANCE 1 1
0x32 ORIGIN 0 1
0x33 CALLER 0 1
  • The ADDRESS instruction gives the address of the currently executing account.

  • BALANCE gives the ether balance of the currently executing account.

  • ORIGIN gives the address of the originator of the transaction that actually led to the execution of the code within the EVM.

  • CALLER gives the caller's address in the context of Solidity, these would be transaction origin (tx.origin) and message sender (msg.sender) respectively.

0x34 CALLVALUE 0 1
0x35 CALLDATALOAD 1 1
0x36 CALLDATASIZE 0 1
0x37 CALLDATACOPY 3 0
  • CALLVALUE in the context of Solidity would be the message value (msg.vale) that you would see in the smart contracts.

0x38 CODESIZE 0 1
0x39 CODECOPY 3 0
0x3a GASPRIZE 0 1
0x3b EXTCODESIZE 1 1
  • CODESIZE gives the size of the code running in the current environment.

  • CODECOPY lets you copy the code running in the current environment to memory.

  • GASPRICE in the context of Solidity; you would see this as transaction.gasPrice, which gives you the price of the Gas in the current environment.

0x3b EXTCODESIZE 1 1
0x3c EXTCODECOPY 4 0
0x3d RETURNDATASIZE 0 1
0x3e RETURNDATACOPY 3 0
0x3f EXTCODEHASH 1 1

This set of instructions lets you query an external contract account.

  • EXTCODESIZE gives you the size of the specified account's code.

  • EXTCODECOPY copies the specified accounts code to memory.

  • RETURNDATASIZE gives the size of the output data from the previous call in this current environment.

  • RETURNDATACOPY copies that return data.

  • EXTCODEHASH gives the hash of the external account's code.

e. Block Information Instructions

Similar to environment key information, EVM also has a set of instructions that gives information about transactions block.

0x40 BLOCKHASH 1 1
0x41 COINBASE 0 1
0x42 TIMESTAMP 0 1
0x43 NUMBER 0 1
0x44 DIFFICULTY 0 1
0x45 GASLIMIT 0 1
  • BLOCKHASH gives the hash of one of the specified 256 most recent complete blocks. If the specified block is not one of the most recent 256 ones, then this instruction returns zero, which is something that has a security implication.

  • COINBASE gives the block's beneficiary address (the address to which the block reward and transaction fees are credited to).

  • TIMESTAMP gets the block's timestamp.

  • NUMBER gets the block's number.

  • DIFFICULTY gets the block's diffiulty.

  • GASLIMIT gets the block's gas limit.

f. Stack, Memory, Storage and Flow Instructions

The next category of instructions are related to the stack memory and storage; load and store operations and also those that affect the control flow.

0x50 POP 1 0
0x51 MLOAD 1 1
0x52 MSTORE 2 0
0x53 MSTORE8 2 0
0x54 SLOAD 1 1
0x55 SSTORE 2 0
  • POP pops an element of the stack.

  • MLOAD and MSTORE load and store from memory.

  • MSTORE8 stores a single byte to memory instead of the whole word.

  • SLOAD and SSTORE load and store words from and to the storage.

The next set of instructions affect the control flow.

0x56 JUMP 1 0
0x57 JUMPI 2 0
0x58 PC 0 1
0x59 MSIZE 0 1
0x5a GAS 0 1
0x5b JUMPDEST 0 0
  • JUMP jumps to the specific location.

  • JUMPI is a conditional jump that jumps depending on the value specified.

  • PC gives you the value of the program counter.

  • MSIZE gives the size of active memory in bytes as of this instruction.

  • GAS gives the amount of available Gas as of this instruction. This is in the context of the Gas that is supplied with the transaction: how much gets consumed and how much is left.

  • JUMPDEST has no effect on the machine state: it does not affect the control flow but it marks a particular destination as being a valid destination for the jump instructions that we talked about.

g. Push Operations

The next set of instructions are specific to the stack. These instructions push operands or place items onto the stack. Depending on the number of items placed, there are 32 such instructions.

0x60 PUSH1 0 1
0x61 PUSH2 0 1
 .     .
 .     .
 .     .
0x7f PUSH32 0 1

PUSH1 pushes a single byte onto the stack, PUSH2 pushes 2 bytes and it goes all the way up to PUSH31. The PUSH32 instruction pushes a full word (32 bytes or 256 bits) onto the stack.

h. Duplication Operations

The next category of instructions that operate on the stack are the duplication operations, which duplicate items that are already on the stack.

0x80 DUP1 1 2
0x81 DUP2 1 2
 .     .
 .     .
 .     .
0x8f DUP16 1 2

DUP1 for example duplicates the first stack item, DUP2 duplicates the first two items on the stack, and it goes all the way up to DUP16.

i. Exchange Operations

The final set of instructions that operate on stack items are the exchange operations. These exchange or swap items that are already on the stack.

0x90 SWAP1 2 2
0x91 SWAP2 3 3
 .     .
 .     .
 .     .
0x9f SWAP16 17 17

SWAP1 exchanges the first and second stack items, SWAP2 exchanges the first and third and so on all the way up to SWAP16 which exchanges the first and 17 $^\text{th}$ stack items.\

j. Logging Operations

These operations append log records from within the execution context of the contract onto the blockchain. We talked about this a bit in the context of the bloom filter in the block header.

0xa0 LOG0 2 0
0xa1 LOG1 3 0
0xa2 LOG2 4 0
0xa3 LOG3 5 0
0xa4 LOG4 6 0

These instructions differ in the number of topics that are specified as being part of the log. So the log itself refers to the event that is fired from within the context of the contract and in the event. The different parameters can be specified as either being indexed or non-indexed.

Indexed parameters go into the topics part of the log and the non-indexed parameters go into the data part of the log.

This differentiates how fast the the parameters or the records can be queried, searched and looked. These instructions are critical to how the contracts actually communicate some of their state to the off-chain interfaces or the off-chain monitoring tools.

k. System Operations

The next set of instructions include instructions that are critical to how the system functions. They allow one to create new contract accounts, call from one account to another in different ways, revert from the current executing context, invalidate some of the things that have happened and so on.

0xf0 CREATE 3 1
0xf1 CALL 7 1
0xf2 CALLCODE 7 1
  • CREATE is used to create a new contract account that has associated code and storage with it. Recall that contract accounts can be created from an EOA by sending a special transaction to the zero address (0x0) or they can also be created from other contracts when they're executed. The address of the newly created account depends on the sender's address and the nonce of that account. So this makes the newly created contracts address dependent on the previous transactions that have executed from the sender's account. This becomes interesting when we talk about the related instruction called CREATE2.

  • CALL allows the current executing context to do a message call into another account. So now there is a caller account that is doing a message call into a callee account. This is interesting because it lets contracts call each other in the executing context.

  • CALLCODE is another call related instruction which lets the caller account call a callee account and lets the callee account execute its code in the context of the state of the caller's account. This distinction is really critical and it has big security implications in some of the future instructions we'll talk about.

0xf3 RETURN 2 0
0xf4 DELEGATECALL 6 1
0xf5 CREATE2 4 1
  • RETURN holds execution and returns the output data.

  • DELEGATECALL is a very interesting instruction part of the call family of instructions which acts very similar to CALLCODE. There is a caller account that calls into the callee account, and the callee account executes its code in the context of the caller's state. The difference here between CALLCODE is that in the case of DELEGATECALL, in the context of Solidity, msg.sender and msg.value of the caller's account is used in the execution context of the callee account.

  • CREATE2 is similar to CREATE and is used to create new contract accounts with associated code and storage. The difference here is that CREATE2 allows you to create accounts with a predictable addresses, unlike CREATE where the address of the newly created contact account dependeds on the nonce. So CREATE2 removes all the transactions that happened from the sender's account so that the address of contracts being generated are predictable. Again, this has big implications in security.

0xfa STATICCALL 6 1
0xfd REVERT 2 0
0xfe INVALID NaN NaN
  • STATICCALL is another instruction in the call family which allows the callee account (that is being called into) to only read the state of the caller account without letting it modify it. This has security implications as well.

  • REVERT holds execution of the current executing context, it returns the data and it returns the remaining Gas that's left behind after consuming all the Gas that was supplied as part of the triggering transaction so far.

  • INVALID (0xfe) is again a special instruction in EVM. It consumes all the Gas that's been supplied as part of the triggering transaction and it is used in the context of some of the static analysis tools that we'll touch upon in later chapters.

0xff SELFDESTRUCT 1 0

The final instruction (0xff) in the EVM instruction set is a special instruction called SELFDESTRUCT. As you can imagine, this holds execution but it also destroys the account of the executing context: the account is registered for later deletion.

This has huge security implications because the contract account that is executing will not exist after the transaction finishes. This is something we will touch upon and some of the security aspects as when we talk about some of the findings and security pitfalls in later chapters.

Gas Costs

We have talked about Gas in the context of transaction, in the context of the block Gas limit and so on... But where it really matters is in the context of Turing complexity and quasi-Turing completeness. The boundedness imposed on the EVM programming language is stemming from the Gas costs that are associated with each of the different EVM instructions

All these instructions have different Gas costs and the reason for that is because each of them has a different requirements when it comes to the computation processing power of the executing Ethereum node, and also the storage requirements, memory accesses and the disk accesses on the real physical hardware that's running the Ethereum node in the context of a miner or anyone else.

When we look at the Gas costs, the simplest instructions like STOP, INVALID and REVERT (that only affect the executing context in a very special way, without having a very high demand or no demand on the processing or the storage of that executing physical hardware), the Gas cost is zero.

For most of the arithmetic, logic and stack instructions, the Gas costs vary between 3 to 5 Gas units. Let's contrast this with some of the more demanding instructions like the call family of instructions, the BALANCE, the EXTCODEHASH, EXPORT, COPY..., those kinds of instructions have a much greater processing requirement from the Ethereum node: these now cost 2600 Gas units.

This again contrast with the memory instructions like MLOAD and MSTORE, which within the context of the EVM are very simple instructions that operate on EVM's internal data structures. These memory instructions cost only 3 Gas units.

However the storage instructions like SLOAD and SSTORE, because they deal with persistent state and have to access the disk or the persistent state within the physical machine of that Ethereum node, cost much more than the memory instructions: SLOAD costs 2100 Gas and SSTORE costs 20000 Gas units.

To set a storage slot costs 20000 Gas. To change that storage value from zero to a non-zero value (and there are optimizations here) costs only 5000 Gas in some of the other situations.

These Gas costs have changed over the duration of the last 5 to 6 years as Ethereum has evolved. These changes happen to prevent some denial of service attacks that have also happened in the past.

This can be researched in the documentation by looking at some of the EIPs that have been created specifically to address the Gas cost of these instructions in some of the most recent upgrades (like the Berlin upgrade) to see why these costs Gas costs were changed for some of these more demanding instructions and the rationale behind it.

These become important because not only they address the optimization aspect when somebody is deploying a contract (Gas usage becomes important because it affects the user experience of the user working or interfacing with these contracts) but from a security perspective (these Gas costs become important from the denial of service context as well).

The final set of instructions where the Gas costs are really high are the CREATE instruction (which is probably the most expensive instruction with a cost of 32000 Gas units; and as you can imagine this is because create results in a new contract account being created, so a lot of the data structures within the EVM context are created, registered, have to be made persistent and so on...) and SELFDESTRUCT (it costs 5000 Gas units).

Previous1.12 Transactions, Messages & BlockchainNext1.14 Transaction Reverts & Data

Last updated 1 year ago

(A more complete and detailed description of the EVM opcodes can be found in . If there is any discrepancy/ambiguity of the contents found here, refer to )

📚
🔷
evm.codes
evm.codes