header-langage
简体中文
繁體中文
English
Tiếng Việt
Scan to Download the APP
Partnered With:

The Design Concept of Ethereum (2)

Read this article in 42 Minutes
Following the second part of Ethereum Design Idea (1).
原文标题:《 Echo | 以太坊的设计理念,Part-2 》
Originally written by Vitalik Buterin
原文编译: kim & 阿剑,以太坊爱好者

This article is a translation of the English version of Ethereum design concept published by V God. The original text is longer, so it is divided into several articles.

Check out Ethereum Design Idea 1 here.

叔块(uncle blocks)奖励


The GHOST protocol is an affordable innovation, first proposed by Yonatan Sompolinsky and Aviv Zohar in October 2013. It was the first serious attempt to solve the problem associated with rapid block production.


Ghost is meant to solve a conundrum: A shorter block time (and therefore faster confirmation) leads to more blocks being "outdated" and thus less secure -- because it takes time for blocks to travel through the network, if miner A digs A block and broadcasts it to the whole network, and on the way to broadcasting, miner B digs A block, then B's block is outdated. And B's mining has no contribution to the security of the network.


In addition, there is A centralization problem: if A is A pool with 30% computing power, and B has 10% computing power. A generates stale blocks 70% of the time (since the other 30% of the time produces new blocks, TA can be assumed to "immediately" get the latest block data without waiting for the block to propagate), while B generates stale blocks 90% of the time. If blocks are produced at short intervals, the rate of obsolescence is high, and A can be mined more efficiently with its greater computing power. Therefore, the block generation is too fast, easy to lead to the network calculation of large mining pool in the fact monopoly of the mining process.


According to Sompolinsky and Zohar, Ghost solves the problem of network security degradation caused by the generation of obsolete blocks in the process of calculating which chain is the longest. That is, not only the parent block and the earlier block, but also the obsolete side-block (in Ethereum, we call it the "tertiary block") is added to calculate which block has the largest total proof of work.


To solve the second problem, the centralization problem, we adopted another strategy: we also offered block rewards for outdated blocks: the reward for finding an outdated block was 7/8 of the base reward for that block; Nephew blocks that contain obsolete blocks receive 1/32 of the base bonus as a bounty. However, transaction fees are not awarded to uncle and nephew.


In Ethereum, an obsolete block can only be included as a tertiary block by a direct descendant block up to 7 generations of its sibling block. The reason for this limitation is that, first of all, if GHOST does not limit the intergenerational distance of obsolete blocks, it will cost a lot of overhead to calculate the validity of obsolete blocks; Second, unlimited and outdated block incentives discourage miners from working on the main chain. In the end, the calculations show that the outdated block reward policy, limited to seven tiers, provides most of the desired effect without adverse effects.


A simulator for measuring centralized risk is visible here


A more advanced discussion can be found here


Note: the "include" here technically takes the form that the parent block references the uncle block's block hash in the block header, and then includes the uncle block's block header in the block body.  

The design decisions of the block time algorithm include:


· Block time 12s:I chose 12 seconds because this is already the shortest time interval longer than the network latency. In a 2013 paper measuring latency on the Bitcoin network, 12.6 seconds was determined as the time it takes for newly generated blocks to propagate to 95% of nodes; However, the paper also points out that propagation time is proportional to block size, so in a faster currency, we can expect propagation time to be significantly reduced. The time between propagations is constant, about 2 seconds. However, for the sake of safety, in our analysis we assume that the propagation of the block takes 12 seconds


·  7 代祖先以内的限制:The idea is to keep only a small number of blocks, while clearing out older blocks. It has been proven that the seventh-generation quotable range can provide much of the desired effect.

·  1 代后裔的限制:(For example, if c = child and p = parent,   c(c(p(p(p(head)))))  Is invalid) : this is also a design goal for simplicity, and the above simulators show that this does not pose much of a risk of centralization. (Note: This sentence is difficult to understand; One possible meaning is that the progeny of a tertiary block cannot act as a tertiary block, i.e. only the first-generation side branches of the main chain can act as a tertiary block.


·  叔块必须是有效的 :The tertiary block must be a valid header, not a valid block. This is also done for simplicity, keeping the blockchain model as a linear data structure (without becoming a DAG). However, it is also a valid method to require tertiary blocks to be valid blocks.


·  奖金分配:7/8 的挖矿基础奖励分配给叔块,1/32 分给侄块,它们交易费用都是 0%。如果费用占多数,从中心化的角度看,这会使叔块激励机制无效;然而,这也是为什么只要我们继续使用 PoW,以太坊就会不断发行以太币的原因。  


Difficulty update algorithm


Ethereum currently implements difficulty updates through the following rules:





The goals of the difficulty update rule are:                                                                                                                                                                                                                                                


Quick update:The time between blocks should be adjusted quickly as the hash power increases or decreases;


Low volatility:If the mining force is constant, the difficulty should not fluctuate wildly;


Simple:The implementation of the algorithm should be relatively simple;


Low memory:Algorithms should not rely on too many historical blocks and should use as few "memory variables" as possible. Suppose you have the latest ten blocks and add up the memory variables stored in the heads of the ten blocks. These blocks can be used for the calculation of the algorithm.


Not to explode:The algorithm should not give miners too much incentive to tamper with the timestamp or the pool to repeatedly add or remove computing forces. Our current algorithm is not ideal in terms of low volatility and blowout resistance. Recently, we plan to change the timestamp parameter to compare it to the parent block and the grandfather block, so miners will only have an incentive to change the timestamp if they dig two blocks in a row.


Gas and cost


All transactions in Bitcoin are roughly the same, so their network costs are modeled in a single unit. Transactions in Ethereum are more complex, so transaction fees need to take into account many aspects of an account, including network bandwidth costs, storage costs, and computing costs. In particular, the Ethereum programming language is Turing-complete, so transactions use any amount of broadband, storage, and computing costs; How much will eventually be used is impossible to reliably predict (because of what is known as the "Turing halting problem") (note: there is no reliable way to say whether any program that can be executed on a Turing machine will terminate in a finite number of steps). Preventing someone from using an infinite loop to carry out a denial-of-service attack was a key goal.

The basic mechanism of Ethereum transaction fees is as follows:


Each transaction must specify the amount of gas it is willing to consume (i.e., specify   startgas  Of) and what you are willing to pay per unit of gas (i.e.   Gasprice ) , the startGas * GasPrice value of Ethereum is deducted from the sender's account at the start of the transaction execution; (Note: here's   startgas  That's what we use now. gaslimit  ).

All operations during the execution of the transaction, including reading and writing to the database, sending messages, and calculating each step, consume a certain amount of gas.

If the trade has completed and the gas consumed is less than the specified limit, the trade is executed normally and the remaining gas is assigned to the variable   gas_rem ; Upon completion of the transaction, the sender will receive the returned gas_rem * gasprice value of Ether, and the reward to the miner is (startgas-gas_rem) * gasprice value of Ether;

If the gas is exhausted during the execution of the transaction, then all the executions are restored as before, but the transaction is still valid, except that the only result of the transaction is to pay the miner the value of the startGas * GasPrice in ethereum, and the rest remains the same.

When a contract sends a message to another contract, a GAS limit can be placed on the child executions caused by that message. If the child runs out of gas, the child is restored as before, but the gas is still consumed. (Note: As of July 9, 2021, this hasn't changed, but it may in the future. See EVM features worth considering for removal.)

All the points mentioned above must be met, for example:


If the transaction does not require a GAS limit to be specified, then the malicious user will send a transaction with a multi-billion step loop. No one can process such a transaction because it can take a long, long time to process; But no one can tell the miners in advance, which can lead to denial-of-service vulnerabilities.

An alternative to strict GAS counts would be time constraints, but they are unlikely to be useful because they are too subjective (some computers are faster than others, and even if everyone's computers are the same there is still a chance they will make a mistake).

The full value of startGas * GasPrice should be set at the beginning of the transaction so that the account does not "go bankrupt" during the execution of the transaction and cannot continue to pay the gas charges. Checking the balance at the same time doesn't work either, because the account can put the balance somewhere else.

If trade execution does not fully recover (roll back) without enough gas, the contract must have strong security measures in place to prevent changes to the contract.

If the subrestriction does not exist, the malicious account can perform denial-of-service attacks against other contracts. An attacker could first reach an agreement with the victim contract and then insert an infinite loop at the beginning of the computation, so that sending a message to the victim contract or any attempt to remedy it would deadlock the entire transaction. (Note: This sentence is also difficult to understand.)

Requiring the transaction sender to pay for gas instead of the contract greatly increases developer maneuverability. Early versions of Ethereum used contracts to pay for gas, which led to a rather serious problem: each contract had to implement a "gatekeeper" code to ensure that each incoming message provided enough ether for the contract to consume.

Gas consumption calculation has the following features:


A base fee of 21,000 gas will be charged for any transaction. These fees cover the cost of running the elliptic curve algorithm, which aims to recover the sender's address from the signature, as well as the hard disk and bandwidth space spent storing the transaction.

The transaction can include an unlimited amount of "data." Some opcodes in the virtual machine allow the contract receiving such a transaction to access the data. The "fixed consumption" rule for data is 4 gas per zero byte and 68 gas per non-zero byte. The reason for this formula is that most of the transaction data sent to the contract by the user consists of a series of 32-byte parameters, most of which have many leading zero bytes. This structure may seem inefficient, but because of the compression algorithm, it is actually very efficient. We want this structure to replace other more complex mechanisms that tightly wrap parameters around the expected number of bytes, resulting in a significant increase in complexity at compile time. This is an exception to the sandwich complex model, but it is also a reasonable model due to the cost-benefit ratio.

The cost of the opcode SSTORE used to set the account store item is: 1) 20,000 gas is consumed when the zero value is changed to a non-zero value; 2) Change a zero value to a zero value, or change a non-zero value to a non-zero value, using 5000 Gas; 3) Change a non-zero value to zero and consume 5000 gas; In addition, 15,000 gas is returned when the trade is executed successfully (i.e. the trade is completed before running out of gas). The refund amount is capped at 50% of the total amount of gas consumed in the transaction. This gives people a small incentive to clear items. We noticed that because of the lack of such incentives, storage space on many contracts was not being used efficiently, resulting in a rapid expansion of stored data. This design provides most of the benefits of a "continuous rent for storage" model without losing the guarantee that a contract, once established, will last forever. Deferred refund is necessary because it prevents a denial-of-service attack: An attacker could send a transaction with a small amount of gas and cycle through a large number of storage items until it runs out of gas, consuming a lot of validation effort without actually clearing the storage or paying a lot of gas. The 50% cap is designed to ensure that miners who package trades can still set an upper limit on the computation time it takes to execute trades.

Note: First of all, the gas consumption of state access opcodes such as SSTORE has changed several times with Ethereum's hard fork. As of July 2021, the latest figures can be found in the Berlin Upgrade Contents Overview; The opcode values will continue to change for the foreseeable future; Second, the gas refund mechanism here, instead of starting the problem of mitigating data bloat, has been shown to exacerbate the problem because people can write a lot of junk data when the gas price is low and get gas when the gas price is high by removing the data. That's how GastOken works. It has been confirmed that the "London" fork will change the gas refund mechanism, as described in the "London" upgrade preview."

There is no cost to the message data provided by the contract. Because no data needs to be physically copied during the message invocation, call data can simply be thought of as a pointer to the parent contract memory that does not change during the execution of the child process.

Memory is an infinitely expendable array. However, every 32 bytes of Memory expended costs 1 gas. Anything less than 32 bytes is counted as 32 bytes. (Note: Memory is generally translated as memory, but in the context of Ethereum, it is one of the three types of data stored in EVM, so it is not translated to indicate its particularity.)

The computation time of some opcodes is highly parameter dependent, and the GAS overhead calculation changes dynamically. For example, the cost of EXP is exponential (10 gas + 10 gas/ byte, i.e., x^0 = 1 gas, x^1... X ^255 = 2 gas, x^255... X to the 65535 = 3 gas, and so on. The cost of copying opcodes (e.g., CALLDATACOPY, CODECOPY, EXTCODECOPY) is 1 gas + 1 gas/32 bytes (rounded; The rules for the LOG opcode are similar. The overhead of Memory extensions is not included here. If it is included, it will become a square attack vector (50000 CALLDATACOPY, each time consuming 50000 gas, its computation amount should be 50000^2, but if not using dynamic charging rules, only ~50000 gas is paid).

If the value is not zero, the opcode CALL (and CALLCODE) consumes an additional 9000 gas. This is because any value transfer causes the history store of the archive node to increase significantly. Note that the operation of   Actual consumption & NBSP; Is 6700; However, on top of this, we impose a gas value that is automatically given to the receiver, which is a minimum of 2300. This is done so that the wallet accepting the transaction has at least enough gas to generate the log. (Note: EVM features worth considering for removal)

Another important part of the GAS mechanism is the economics of the Gas price itself. In Bitcoin, the default approach is purely voluntary charging, with miners acting as gatekeepers and dynamically setting minimum charges. Ethereum allows transaction senders to set any number of GAS. This approach is popular in the Bitcoin community because it is a manifestation of a "market economy" that allows miners and traders to set prices based on supply and demand. The problem with this approach, however, is that transaction processing does not follow market principles. Although you can think of transaction processing as a service provided by the miner to the sender (which sounds intuitive), in reality every transaction processed by the miner must be processed by every node in the network, so most of the cost of transaction processing is borne by the third party, not the miner who decides whether to process it. Thus, the "tragedy of the Commons" problem is likely to occur.

At the moment, in the absence of clear information about what miners are doing in practice, we are taking a very simple and fair approach: a voting system that sets the amount of gas that can be consumed in a single block. Miners are entitled to change the gas cap of the latest block by 0.0975% (1/1024) as the gas cap of the current block. So the final gas cap should be the median set by the miners. We hope to use the soft fork approach in the future to use more accurate algorithms.

The virtual machine


The Ethereum virtual machine is the engine that executes the transaction code and is at the heart of what sets Ethereum apart from other systems. Note that virtual machines should be considered separately from the contract and message model. For example, the signExtend opcode is a function of the virtual machine, but the fact that "a contract can call another contract and specify the GAS limit for the child calls" is part of the Contract and Message Model.

The design goals of EVM are as follows:


Simple:Opcodes should be minimal and low-level; As few data types as possible; The virtual machine has as few structures as possible;

The results were clear:In the VM specification, there is no room for ambiguity, and the result should be completely definitive. In addition, the calculation steps should be accurate so that the consumption of GAS can be measured;

Save space:EVM components should be as compact as possible;

Specialize for intended use:Applications built on the VM should be able to handle 20-byte addresses, 32-bit custom encryption values, modular arithmetic for custom encryption, and the ability to read block and transaction data and interact with state;

Simple and safe:To keep the VM from being exploited, it should be easy to model the cost of gas consumption;

Optimize friendliness:It should be easy to optimize so that just-in-time compilation (JIT) and accelerated versions of the VM can be built.

At the same time, EVM also has the following special design:


The difference between temporary and permanent storage:


Let's first look at what is temporary storage versus permanent storage. Temporary storage: Exists in each instance of the VM and disappears when the VM finishes execution. Permanent storage: Existing in the blockchain state layer. Suppose you execute the following tree (S for permanent storage, M for temporary storage) :
A calls B;
B set up & have spent B.S [0] = 5, B.M [0] = 9 & have spent ;
B calls C;
C calls B.

At this point, if B tries to read   B.S[0]  It's going to get the data that B put in before, which is 5; But if B tries to read   B.M[0]  , it will get 0 because B.M is temporary storage and is read as a new instance of the virtual machine. In an inner call, if you set   B.M[0] = 13  And & have spent B.S[0] = 17  The outer call of the outer call of the outer call of the outer call of the outer call of the outer call. B.M[0] = 9  (This value was set in the last instance of the same VM execution),   B.S[0] = 17  . If B's external call ends and A calls B again, you will see   B. m [0] = 0, B. s [0] = 17  . The purpose of this distinction is: 1. Each execution instance is allocated memory space and is not compromised by loop calls, which makes secure programming easier. 2. Provide a form of memory that can be manipulated quickly: Since the tree needs to be modified, storage updates are necessarily slow.

Stack/Memory mode:In the early days, there were three types of computing state (besides a program counter that pointed to the next instruction) : stack (stack, a 32-byte standard LIFO stack), memory (a temporary array of bytes that could be extended indefinitely), and storage (storage, permanent storage). On the temporary storage side, the alternative to stack and memory is the memory-only paradigm, or a hybrid of registers and memory (the two are not very different, registers are essentially memory). In this case, each instruction has three arguments, for example:   ADD R1 R2 R3: M[R1] = M[R2] + M[R3]  . The reason for choosing the stack paradigm is obvious; it shrinks the code by a factor of four.


Word size 32 bytes:In most structures, such as Bitcoin, the word size is 4 or 8 bytes. 4 or 8 bytes is too limited to store addresses and encrypt computations. However, it is difficult to establish a safe GAS model without limiting the size. 32 bytes is an ideal size because it is large enough to store the large numbers and addresses needed by many cryptographic algorithms without becoming too large to be efficient.

We have our own virtual machine:Our virtual machine is developed in languages such as Java, Lisp, and Lua. We decided it was worthwhile to develop a professional VM because: 1) our VM specification is much simpler than many other VMs because other VMs pay less for complexity, which means they are more likely to become complex; However, every extra bit of complexity we add to our solution creates barriers to intensive development and introduces potential security flaws, such as consensus errors, that make our complexity costly; 2) Our VM is more specialized, such as supporting 32 bytes; 3) We will not have complex external dependencies, which will cause us to fail to install; 4) Perfect review mechanism, which can be specific to special safety needs; Even if you use an external VM, you won't save much work.

A variable and extensible memory size is used:Fixed memory size is an unnecessary restriction; too small or too large is not appropriate. If the memory size is fixed, checking whether the access is out of bounds every time the memory is accessed is obviously not very efficient. Call depth limit: Many programming languages crash because call depths are too deep before memory runs out. So using block gas caps alone as a limit is not enough.

No type:Just for brevity. However, DIV, SDIV, MOD, SMOD use signed or unsigned opcodes (it turns out that for opcodes ADD and MUL, signed and unsigned are equivalent); Converting to a fixed-point operation is easy in all cases, for example, a * b - > at 32-bit length; (a * b) / 2^32, a / b -> a * 2^32 / b  , +, -, and * are invariant under integers.

Note: There is also the following paragraph in the original translation, but the corresponding paragraph has been deleted from the current version:           There is no limit to stack size: no special reason! In many cases, this design is not absolutely necessary; Because the overhead of GAS and the block GAS cap always act as a cap on the consumption of each resource.  

The function and purpose of some of the opcodes in this VM is easy to understand, but some are not. Here are some specific reasons:

ADDMOD MULMOD:In most cases,   mulmod(a, b, c) = a * b % c  , but in the elliptic curve algorithm, the 32-byte module operation is used, which is executed directly. a * b % c  It's actually executing. ((a * b) % 2^256) % c  , you get completely different results. Executes the   of a 32-byte value in a 32-byte space; a * b % c  Consensus calculations are difficult and cumbersome.

SIGNEXTEND:The signExtend opcode is used to facilitate type conversions from large signed integers to small signed integers. Small signed integers are useful because future just-to - compile virtual machines may be able to detect long-running blocks of code that primarily deal with 32-byte integers. Small signed integers speed up processing.

SHA3:In Ethereum code, SHA3 is widely used as a secure, high-intensity hash mapping method for data of variable length. Typically, a Hash function is needed when using storage to prevent malicious collisions, and it is also needed when validating Merkle trees and similar Ethereum data structures. Importantly, hash functions similar to SHA3, such as SHA256, ECRECVOR, RIPEM160, are included not as opcodes, but as pseudo-contracts. The purpose is to put them in a separate category if and when we come up with an appropriate "native plug-in" system in the future, we can add more such contracts without the need to extend the opcodes.


ORIGIN:The Origin opcode is provided by the sender of the transaction, and its main purpose is to allow the contract to refund the gas paid.


COINBASE:The main functions of Coinbase are: 1) allowing sub-currencies to contribute to cyber security; 2) Enable Miners as a decentralized economy to set up sub-consensus based applications such as SchellingCoin.


PREVHASH:Prevhash can be used as a semi-secure random source. In addition, it allows contracts to evaluate the proof of state of the Merkle tree for a block above the Evalute without the need for a highly complex recursive "Ethereum Light Client" structure.


EXTCODESIZE EXTCODECOPY:The main purpose is to let contracts examine other contracts' code against templates and even emulate them before interacting with them.see


JUMPDEST:JIT virtual machines are easier to implement when jump destinations are limited to a few indexes (in particular, the computational complexity of dynamic jumps is O(log(number of valid challenge goals)), whereas static jumps are always constant). Therefore, we need to: 1) restrict the destination of the valid variable jump; 2) Incentives to use static rather than dynamic jumps. To achieve these two goals, we set the following rules: 1) a jump that follows a push can jump anywhere, not just another jump; 2) Other jumps can only jump to Jumpdest. Restrictions on the jump are required so that you can determine whether the current jump is static or dynamic by looking at the previous action in the code. The lack of demand for static jumps is what motivates their use. Disabling jumping into push data also speeds up compilation and execution of the JIT virtual machine.


The LOG:Log is a LOG of events.


CALLCODE:This opcode allows a contract to use its own storage entry and call "functions" of other contracts in a separate stack space and memory. This allows for flexible implementation of standard library code on the blockchain.


SELFDESTRUCT:Allows a contract to delete itself if it no longer needs to exist. The SELFDESTRUCT is not executed immediately, but after the transaction has completed. This is because allowing SELFDESTRUCT to be rolled back after execution greatly increases the complexity of the cache and is not conducive to an efficient VM implementation.


PC:Although in theory does not need the PC operation code, because all PC operation code examples can be according to the actual will push the operation indexes to join the program counter to take the place of implementation, but using a PC can create a separate code (can copy and paste to compile the function of other contracts, if they ended up with different indexes, will not be interrupted).


Welcome to join the official BlockBeats community:

Telegram Subscription Group: https://t.me/theblockbeats

Telegram Discussion Group: https://t.me/BlockBeats_App

Official Twitter Account: https://twitter.com/BlockBeatsAsia

举报 Correction/Report
Choose Library
Add Library
Cancel
Finish
Add Library
Visible to myself only
Public
Save
Correction/Report
Submit