Code has been added to clipboard!

Solidity Ethereum Virtual Machine

Solidity Ethereum Virtual Machine Main Tips

  • This tutorial will introduce you to the Ethereum Virtual Machine (EVM) runtime environment.
  • Solidity is specifically made for handling operations with the EVM.

Solidity Ethereum Virtual Machine

The EVM is the runtime environment used for Ethereum's smart contracts.

Being completely isolated, it runs the code without any access to the network, filesystem or any other process.Because of this, smart contracts have limited access to even other smart contracts.

In this tutorial, we will go through the various functions of EVM.

Note: The removal of old contracts might not be implemented by Ethereum clients . Also, archive nodes may choose to keep the contract storage and code indefinitely.

Solidity Ethereum Virtual Machine Accounts

When it comes to Ethereum, there are two kinds of accounts and both of them sharing the same address space:

  • External accounts - controlled by public-private key pairs, i.e. the people
  • Contract accounts - controlled by the code that is being stored with the account itself

The external account's address is determined by the public key while the address of a contract is determined as the contract is created (deriving from the address of the creator and the number of transactions that have been sent from that particular address, the so-called “nonce”).

Regardless, the two account types are treated similarly by the EVM. Also, each account has a persistent key-value store which maps 256-bit words to 256-bit words referred to as storage.

Additionally, each account has a balance in Ether (“Wei”, more precisely) that can be modified by sending transactions which include Ether.

Note: External accounts cannot be removed from the state.

Solidity Ethereum Virtual Machine Transactions

Transactions are messages sent between accounts (may be the same or the special zero-account, as seen below). This may include the binary data, referred to as the payload, or Ether.

In cases that the target account contains data, the code is executed with the payload being used as the input data.

Some targets may be zero-accounts, distinguished by them having the address 0. In this case, the transaction will create a contract. As mentioned before, the address of this new contract would be derived from the address of the creator and the  "nonce", the number of transactions sent prior to the one creating that particular contract.

Knowing this, we can see that to create a contract, you have to send the code that returns the code of the contract, not send the code of the contract itself.

Solidity Ethereum Virtual Machine Gas

By definition, Gas is the internal pricing for running a transaction or contract in Ethereum.

Once created, each transaction gets charged with some amount of Gas, in order to limit the amount of work required for the execution of the transaction and to provide payment for this execution.

Throughout the execution, EVM gradually uses up the gas according to specific rules. The value that is gas price is set by the creator of the transaction, having to pay the value of gas_price*gas from the account that the transaction is sent from.

In case some gas is left once the execution is done, it is similarly refunded. In the scenario that gas is used up at any point of executing the transaction, it triggers the out-of-gas exception, reverting all changes made during the execution, to the current frame of the call state.

Solidity Ethereum Virtual Machine Memory

EVM utilizes three areas of memory when at work.

This includes:

  • Storage
  • Memory
  • The Stack

Let's look at these memory areas in more depth.

Storage

The persistent memory that every account has is called storage.

It is a key-value store which maps 256-bit words to 256-bit words. It cannot be enumerated from inside a contract, in addition to being overall difficult to read and modify.

Overall, contracts can neither read, nor modify, any storage, except for its own.

Memory

The memory area simply referred to as memory, is received in the form of a freshly cleared instance on each message call.

It is linear and possible to address at the byte level. However, it is limited to reads at the 256-bit width, while writing can be either 8 or 256-bit wide.

Memory gets expanded by a word (256 bits) every time a previously untouched memory word is accessed. The expansion is paid for with gas.

Due to scaling quadratically, it becomes costly as it grows.

The Stack

Firstly, it should be noted that EVM is not a register, but a stack machine since all operations are performed at the area referred to as the stack.

The maximum number of elements it can contain is 1024 and it stores words that are 256 bits in size. The access to the stack is limited to the top end. This is because it is possible to copy one of the top 16 elements or swap the top element with one of the 16 elements below.

All other operations simply take the top elements, the number of the elements depending on the operation, from the stack, push the result on the stack.

It is also possible to move stack elements to the memory or storage, but to access arbitrary elements, the top of the stack must still be removed first.

Solidity Ethereum Virtual Machine Instruction Set

The EVM's instruction set is kept minimal so incorrect implementations with the potential of causing consensus problems can be avoided.

All instructions operate using the basic data type, 256-bit words. The usual bit, logical, comparison and arithmetic operations are present. Jumps, conditional and unconditional, are viable. Additionally, the relevant properties of the current block, such as its timestamp and number, can be accessed by contracts.

Solidity Ethereum Virtual Machine Message Calls

It is possible for contracts to call other contracts, or send Ether to external (non-contract) accounts using message calls. Message calls have many similarities to transactions, such as having a target, a source, data payload, gas, Ether and return data. Actually, each transaction consists of a top-level message call that in turn has the ability to create further message calls.

A contract will decide how much of its remaining gas is to be sent using inner message call in addition to how much of it should be retained. In case an out-of-gas exception happens during the execution of the inner call (or any other exception), an error value will put onto the stack, signaling the exception. If this happens, only the gas that was sent together with the call is used up. In Solidity, the calling contract will cause a manual exception by default in situations like this, so exceptions “bubble up” the call stack.

As mentioned previously, the contract that was called (it can be the same contract like the one that calls) receives a freshly cleared instance of memory and has access to the call payload - which will be provided in a separate area that is called the calldata. After the execution has been finished, it can return data that is going to be stored in a location in the caller’s memory preallocated by the caller (the contract that calls).

Calls are limited to a depth of 1024, which means that for more complicated operations, loops are preferred instead of recursive calls.

Solidity Ethereum Virtual Machine Delegatecall / Callcode and Libraries

delegatecall is a special version of the message call.

It is largely identical to the message call. The main difference is that the code of the targeted address is executed in the calling contract's context. Because of this, msg.value and msg.sender do not change. This is useful because it allows code to be loaded from a different address at runtime, while the storage, calling address and the balance still refer to the calling contract.

This makes libraries in EVM viable, making it possible to have reusable library code, which can be implemented into another contract's storage. For example, this can be used to implement a complex data structure without the need to write the same lines of code again.

Solidity Ethereum Virtual Machine Logging

EVM allows storing data in a special, indexed data structure, mapping all the way up to the block level.

As mentioned earlier, you can log transactions using events. Although contracts themselves cannot access log data, the logs can be accessed from outside the blockchain.

Because a certain amount of log data gets stored in bloom filters, this data can be searched for in an efficient and cryptographically secure way. This makes it so "light clients" (network peers that do not download the whole blockchain) can access the logs.

Solidity Ethereum Virtual Machine Creating Contracts

It is also possible for contracts to create other contracts using a special opcode.

For example, these contracts do not simply call zero addresses. The main difference between create calls and normal message calls is that the payload data gets executed, the result stored and the caller which sent the create call gets the address of the new contract onto its stack.

Solidity Ethereum Virtual Machine Creating Self-Destruct

The only way to remove code from the blockchain is using the selfdestruct operation.

When it is used, the stored Ether is sent to the designated address and the code and storage are removed from the state.

Warning: it's possible for the contract code not to have the selfdestruct operation, but still perform it using delegatecall or callcode.