We are glad you made it to the second part of our dissection of the ethereum white paper. Read on to uncover the rest of the ethereum white paper document.
Ethereum was built around the central focus of creating a protocol for building a variety of decentralized applications with numerous use cases.
They provide a Turing complete programming language where development time, security and interaction between dapps (decentralized apps) are important. A Turing complete programmable blockchain allows a wide variety of smart contracts to be developed which are much more sophisticated than those offered by Bitcoin.
Ethereum is designed on the following five principles.
Ethereum is built as a protocol that is simple and has a vision of being open to all, even at the
cost of data storage and time inefficiency. Any average programmer should be able to pick the
workflow and implement projects with ease.This helps in fully realizing the unprecedented
potential of Blockchain and Cryptocurrency.
The Turing completeness of Ethereum helps in creating any smart contract that can be
mathematically defined. Currency, financial derivatives or your very own Skynet, anything can be built. However if you do plan on building Skynet, you might need to have an array of many interlocking contracts and feed them with enough gas to keep the smart contract running.
Ethereum is designed such that all parts of the protocol can be separated into individual units. Even if somebody makes a small protocol modification in one place, other parts of the application stack would be seemingly unaffected and continue to work without further modification.
Innovations like Ethash, modified Patricia trees and RLP (which will be discussed in future posts) are implemented as separate, feature complete libraries. Ethereum development is done so as to benefit the whole cryptocurrency system rather than just itself.
Constructs of the Ethereum protocol are not set in stone, although modifications to high-level constructs will only be done judiciously.
Non-discrimination and non-censorship
Being a true open for all protocol, any and all kinds of applications can be developed using Ethereum. The regulatory mechanisms used in Ethereum are used to restrict and minimize the harm to the ecosystem rather than restrict a specific category of applications.
For instance, you can run an infinite loop script as long as you pay necessary and relevant charges to the miners for running your code.
In Ethereum, the state is made up of objects called “accounts” where each account has a 20-byte public address. State transitions are transfers of value and information between two or more accounts. An Ethereum account contains the following four fields.
- Nonce; this is a counter that ensures each transaction can only be processed once
- The account’s current Ether balance
- The account’s Contract code, (if present, applicable to smart contracts)
- The account’s Storage (empty by default)
Ether is the main fuel used in Ethereum and is used for transaction fees also known as Gwei.
There are two types of accounts namely :
- Externally owned accounts; controlled by Private keys : Have no inherent code. Messages are sent by creating and signing a transaction.
- Contract accounts; controlled by Contract code : Code activates depending on the content of the received message and further process like reading & writing into internal storage, sending other messages or creating contracts can be activated.
The second type of account is used by a cryptocurrency exchage :Blockchain Board of Derivatives in its non-custodial smart contract wallet system.
Smart contracts are thus autonomous agents that live inside the Ethereum environment and execute code when conveyed by a transaction or a message. Such contracts have direct control over their ether balance and their own key store.
Transaction in Ethereum is essentially a signed and encrypted data package that stores a message to be sent from an externally owned account.
Typical transactions contain the following:
- The recipient of the message (Public Key of the recipient)
- Signature identifying the sender (Private Key of the sender)
- The amount of ether to transfer from the sender to the recipient
- An optional data field
- A STARTGAS value, representing the maximum number of computational steps the transaction execution is allowed to take
- A GASPRICE value, representing the fee the sender pays per computational step
Let us break down these individual points. The first three are standard fields present in every cryptocurrency. The data field has no default function but can be used by a contract to access the data. For instance, if a contract is functioning as a domain registration service, then it may wish to interpret the data being passed to it as containing two “fields”, the first field being a domain to register and the second field being the IP address to register the domain to. The contract would read these values from the message data and appropriately place them in storage.
STARTGAS and GASPRICE fields are crucial for Ethereum’s anti-denial of service model. In order to prevent infinite loops or other computational wastage, each transaction is required to set a limit to the number of computational steps it can use. The fundamental unit of computation is “gas”. Usually, a computational step costs 1 gas, but some operations cost higher amounts of gas because they are more computationally expensive or increase the amount of data that must be stored as part of the state.
There is a fee of 5 gas for every byte in the transaction data. The fee system causes an attacker to pay proportionately for every resource that they consume, including computation, bandwidth and storage. Hence, any transaction that leads to high network consumption naturally leads to a higher gas fee.
In simple terms, gas paid is directly proportional to the number and complexity of computations done on the blockchain.
Contracts can send messages to other contracts.
Typical messages contain:
- The sender of the message
- The recipient of the message
- The amount of ether to transfer with the message
- An optional data field
- A STARTGAS value
A message is similar to transaction except that messages are created by a contract and not an externally owned accounts. A message is produced when a contract executing code executes the CALL opcode, producing and executing a message.
The message is received by the recipient account which then runs its code. In this manner, contracts can enact in relationships with other contracts in a way similar to externally owned accounts.
The gas allocation assigned by a contract applies to both the gas consumed by transaction and all sub-executions.
Let us understand the same with an example.
@A is an externally owned account
@B is a contract
@A sends @B a transaction with 1000 gas.
@B consumes 600 gas and sends a message to @C.
The internal execution of @C consumes 300 gas.
This implies that the contract @B can only spend another 100 gas on computation / message / transaction before running out of gas.
Ethereum State Transition Function
As mentioned in part 1 of the series, you might recall the state transition function
APPLY(S,TX) -> S’
Further steps are taken from the white paper and are pretty much self-explanatory:
- The transaction must have the right number of values, the signature must be valid and the nonce should match the nonce in the sender’s account. If it does not comply, throw an error.
- The transaction fee is calculated as STARTGAS * GASPRICE, the sending address can be determined from the signature. Subtract the fee from the sender’s balance and increment the sender’s nonce. If there is not enough balance to spend, throw an error.
- Initialize GAS = STARTGAS, and a certain quantity of gas per byte is taken off to pay for the bytes in the transaction.
- Transfer the transaction value from the sender’s account to the receiving account. If the receiving account does not yet exist, create it. If the receiving account is a contract, run the contract’s code either to completion or until the execution runs out of gas.
- If the value transfer failed because the sender did not have enough money, or the code execution ran out of gas, revert all state changes except the payment of the fees, and add the fees to the miner’s account. The payment of fees cannot be reverted as miner expends energy to facilitate the transaction.
- Otherwise, refund the fees for all remaining gas to the sender, and send the fees paid for gas consumed to the miner.
Assume the contract code to be the following:
self.storage[calldataload(0)] = calldataload(32)
The contract is actually written in low-level EVM code but the above example is written in Serpent.
Now let us consider an example:
The contract’s storage is initially empty and a transaction is sent with 10 ether value, 2000 gas, 0.001 ether gasprice, and 64 bytes of data, with bytes 0-31 representing the number 2 and bytes 32-63 carrying the string CHARLIE.
The state transition function process in this scenario is as follows. These steps are similar to the ones mentioned in the generic example above.
- Check that the transaction is valid and well-formed.
- Check that the transaction sender has at least 2000 * 0.001 = 2 ether. If it is, then subtract 2 ether from the sender’s account. (Since we have to use STARTGAS * GASPRICE as the formula)
- Initialize gas = 2000; assuming the transaction is 170 bytes long and the byte-fee is 5, subtract 850 (170*5) so that there is 1150 (2000-850) gas left.
- Subtract 10 more ether from the sender’s account, and add it to the contract’s account.
- Run the code. In this case, this is simple: it checks if the contract’s storage at index 2 is used, notices that it is not, and so it sets the storage at index 2 to the value CHARLIE. Suppose this takes 187 gas, so the remaining amount of gas is 1150 – 187 = 963
- Add 963 * 0.001 = 0.963 ether back to the sender’s account, and return the resulting state.
This concludes the steps that are undertaken in the whole process.
If there was no contract at the receiving end of the transaction, then the total transaction fee would simply be equal to the provided GASPRICE multiplied by the length of the transaction in bytes, and the data sent alongside the transaction would be irrelevant.
In this case, all gas would be utilized by a miner to provide no result as any contract does not exist.
Messages and transactions work on similar terms when it comes to reverts: if a message execution runs out of gas, then that message’s execution, and all other executions triggered by that execution, revert, but parent executions do not need to revert.
This implies that it is “safe” for a contract to call another contract as if A calls B with G gas then A’s execution is guaranteed to lose at most G gas. However, parent executions outside of contracts do not revert.
Also, there is an opcode, CREATE, that creates a contract. Its execution mechanics are generally similar to CALL, with the exception that the output of the execution determines the code of a newly created contract.
We will delve into opcode in further detail in our future in-depth technical blog posts.
The code in Ethereum contracts is written in a low-level, stack-based bytecode language, referred to as “Ethereum Virtual Machine code” or “EVM code”. EVM code is essentially a series of bytes and each byte is an operation.
“Code execution is an infinite loop that consists of repeatedly carrying out the operation at the current program counter (which begins at zero) and then incrementing the program counter by one, until the end of the code is reached or an error or STOP or RETURN instruction is detected.”
The operations have access to three types of space in which to store data:
- Stack, a last-in-first-out container to which values can be pushed and popped like a typical stack.
- Memory, an infinitely expandable byte array.
- Storage, a key/value store. Unlike stack and memory, which resets after computation ends, storage persists for the long term.
The code also has the ability to access the value, the sender, the data of the incoming message and the block header as well. The code can also return a byte array of data as output.
The execution model of EVM code is quite simple. We will further explore it in the below steps.
While the Ethereum virtual machine is running, its full computational state can be defined by the tuple. A tuple consists of block_state, transaction, message, code, memory, stack, pc and gas.
Here, block_state is the global state containing all accounts and includes balances and storage.
At the start of every round of execution, the current instruction is found by taking the pc-th byte of code (or 0 if pc >= len(code)) which means pc is considered to be zero when it is greater than or equal to the length of the code.
Each instruction has its own definition on how it would affect the tuple.
ADD pops two items off the stack, pushes their sum, reduces gas by 1 and increments pc by 1 (typical working of a stack)
SSTORE pops the top two items off the stack and inserts the second item into the contract’s storage at the index specified by the first item.
There are many ways to optimize EVM execution via just-in-time compilation, a basic implementation of Ethereum can be done in a few hundred lines of code.
Ethereum blockchain is more or less similar to the Bitcoin blockchain with a few subtle differences.
The main difference between Ethereum and Bitcoin with regard to the blockchain architecture is that, unlike Bitcoin (which only contains a copy of the transaction list), Ethereum blocks contain a copy of the transaction list, the most recent state, the block number and the difficulty.
The basic block validation algorithm in Ethereum can be explained in the following steps:
- Check if the previous block referenced exists and is valid.
- Check that the timestamp of the block is greater than that of the referenced previous block and less than 15 minutes into the future.
- Check that the block number, difficulty, transaction root, uncle root and gas limit (various low-level Ethereum-specific concepts which will be covered later) are valid.
- Check that the proof of work on the block is valid.
- Let S be the state at the end of the previous block. (recall this being discussed and explained in the previous blog post)
- Let TX be the block’s transaction list, with n transactions. For all i in 0…n-1, set S[i+1] = APPLY(S[i],TX[i]). If any applications returns an error, or if the total gas consumed in the block up until this point exceeds the GASLIMIT, return an error.
- Let S_FINAL be S[n], but adding the block reward paid to the miner (S_FINAL =S[n]+Block reward). The reward is awarded once a miner completes mining a block successfully.
- Check if the Merkle tree root of the state S_FINAL is equal to the final state root provided in the block header. If it is, the block is valid; otherwise, it is not valid. (Merkle tree and validation with the block header is explained with relevant pictures in the previous blog post)
The approach of storing the entire state within each block might seem inefficient at first but is comparable to that of Bitcoin.
The state is stored in the tree structure and after every block, only a tiny part of the tree needs to be changed. This implies that between two adjacent blocks, the vast majority of the tree should be the same. The data can be stored once and referenced twice using pointers (hashes of subtrees).
A special kind of tree known as a “Patricia tree” is used to accomplish this, including a modification to the Merkle tree concept that allows for nodes to be inserted and deleted in an efficient manner.
Additionally, because all of the state information is part of the last block, there is no need to store the entire blockchain history.
A commonly asked question is “where” contract code is executed, in terms of physical hardware.
The process of executing contract code is defined in the state transition function itself, which is part of the block validation algorithm. If a transaction is added into block B the code execution spawned by that transaction will be executed by all nodes that download and validate block B, either now or in the future.
This marks the end of Part 2 of the Ethereum white paper. In the next part, we will discuss real-time applications of the Ethereum protocol and the ecosystem.
Part 3 available here.