成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

深入理解以太坊虛擬機(jī) (一) 中英對(duì)照

BLUE / 3190人閱讀

摘要:以太坊的工具鏈還非常新,深入理解可以幫助你和他人實(shí)現(xiàn)一些很棒的工具。到目前為止依舊不是很清楚,別擔(dān)心走讀字節(jié)碼并沒(méi)有想象中的那么困難。編譯器本可以通過(guò)得到相同的結(jié)果,顯然后者的字節(jié)碼要短一些。

原文: Diving Into The Ethereum VM
作者: Howard
譯者: 187J3X1 


Solidity offers many high-level language abstractions, but these features make it hard to understand what’s really going on when my program is running. Reading the Solidity documentation still left me confused over very basic things.

Solidity提供了許多高級(jí)語(yǔ)言的特性,但這些高級(jí)特性使得要想去理解底層程序是如何運(yùn)行過(guò)程變得困難。即使讀了Solidity的官方文檔,我依然對(duì)一些基礎(chǔ)的內(nèi)容感到困惑。

What are the differences between string, bytes32, byte[], bytes?

Which one do I use, when?

What’s happening when I cast a string to bytes? Can I cast to byte[]?

How much do they cost?

stringbyte32,byte[],bytes這些類(lèi)型到底有什么區(qū)別?

什么情景該選擇使用哪一個(gè)?

string類(lèi)型轉(zhuǎn)換為bytes類(lèi)型時(shí)會(huì)發(fā)生什么? 能轉(zhuǎn)換成 byte[]類(lèi)型嗎?

使用它們各有多大的代價(jià)?

How are mappings stored by the EVM?

Why can’t I delete a mapping?

Can I have mappings of mappings? (Yes, but how does that work?)

Why is there storage mapping, but no memory mapping?

映射類(lèi)型(mappings)在EVM中時(shí)被如何存儲(chǔ)的?

能刪除一個(gè)映射?

能創(chuàng)建映射的映射嗎?

為什么只有存儲(chǔ)空間的映射,沒(méi)有內(nèi)存空間的映射?

How does a compiled contract look to the EVM?

How is a contract created?

What is a constructor, really?

What is the fallback function?

EVM眼中經(jīng)過(guò)編譯的合約是什么樣子?

合約如何創(chuàng)建?

構(gòu)造函數(shù)是什么?

fallback函數(shù)是什么?

I think it’s a good investment to learn how a high-level language like Solidity runs on the Ethereum VM (EVM). For couple of reasons.

Solidity is not the last word. Better EVM languages are coming. (Pretty please?)
The EVM is a database engine. To understand how smart contracts work in any EVM language, you have to understand how data is organized, stored, and manipulated.
Know-how to be a contributor. The Ethereum toolchain is still very early. Knowing the EVM well would help you make awesome tools for yourself and others.
Intellectual challenge. EVM gives you a good excuse to play at the intersection of cryptography, data structure, and programming language design.

我認(rèn)為去學(xué)習(xí)Solidity這樣的高級(jí)語(yǔ)言在以太坊虛擬機(jī)(EVM)中的運(yùn)行過(guò)程是項(xiàng)非常棒的投資。至少有以下這些好處:

Solidity并不是終點(diǎn)。更好的EVM語(yǔ)言已經(jīng)在路上了。

EVM是一個(gè)數(shù)據(jù)庫(kù)引擎,要想理解智能合約是如何工作的,首先要理解合約數(shù)據(jù)是如何組織、存儲(chǔ)、操作的。

可以成為貢獻(xiàn)者。以太坊的工具鏈還非常新,深入理解EVM可以幫助你和他人實(shí)現(xiàn)一些很棒的工具。

可以提升思維。EVM能讓你深入研究密碼學(xué)、數(shù)據(jù)結(jié)構(gòu)、程序設(shè)計(jì)。

譯注:Solidity是高級(jí)語(yǔ)言,定制的編譯器可以將這種高級(jí)語(yǔ)言轉(zhuǎn)化為EVM能理解的一串二進(jìn)制編碼,所以只要能生成這種二進(jìn)制碼,并不一定限定在Solidity。有點(diǎn)類(lèi)似與JAVA利用JVM實(shí)現(xiàn)跨平臺(tái)。

In a series of articles, I’d like to deconstruct simple Solidity contracts in order to understand how it works as EVM bytecode.

An outline of what I hope to learn and write about:

The basics of EVM bytecode.

How different types (mappings, arrays) are represented.

What is going on when a new contract is created.

What is going on when a method is called.

How the ABI bridges different EVM languages.

My final goal is to be able to understand a compiled Solidity contract in its entirety. Let’s start by reading some basic EVM bytecode!

在接下來(lái)的一些列文章中,我會(huì)以一些簡(jiǎn)單的Solidity編寫(xiě)的合約為例展示其在EVM中是如何工作的。

以下是我希望學(xué)到的知識(shí)大綱:

EVM字節(jié)碼基礎(chǔ).

不同數(shù)據(jù)結(jié)構(gòu)的組織方式,比如映射和數(shù)組(mapping,arrays)

合約創(chuàng)建時(shí)發(fā)生了什么.

方法被調(diào)用時(shí)發(fā)生了什么.

ABI是如何橋接不同的EVM語(yǔ)言的.

我的最終目標(biāo)是能夠完全理解智能合約的工作原理,讓我們從EVM字節(jié)碼開(kāi)始吧

This table of EVM Instruction Set would be a helpful reference.

你可以隨時(shí)查看EVM支持的指令集以獲得幫助。

譯注:指令集對(duì)應(yīng)于源碼 core/vm/opcodes.go

A Simple Contract

Our first contract has a constructor and a state variable:
第一個(gè)合約的例子包含一個(gè)構(gòu)造函數(shù)和一個(gè)狀態(tài)變量

// c1.sol
pragma solidity ^0.4.11;

contract C {
    uint256 a;

    function C() {
      a = 1;
    }
}

Compile this contract with solc:
使用solc來(lái)編譯這個(gè)合約:

$ solc --bin --asm c1.sol

======= c1.sol:C =======
EVM assembly:
    /* "c1.sol":26:94  contract C {... */
  mstore(0x40, 0x60)
    /* "c1.sol":59:92  function C() {... */
  jumpi(tag_1, iszero(callvalue))
  0x0
  dup1
  revert
tag_1:
tag_2:
    /* "c1.sol":84:85  1 */
  0x1
    /* "c1.sol":80:81  a */
  0x0
    /* "c1.sol":80:85  a = 1 */
  dup2
  swap1
  sstore
  pop
    /* "c1.sol":59:92  function C() {... */
tag_3:
    /* "c1.sol":26:94  contract C {... */
tag_4:
  dataSize(sub_0)
  dup1
  dataOffset(sub_0)
  0x0
  codecopy
  0x0
  return
stop

sub_0: assembly {
        /* "c1.sol":26:94  contract C {... */
      mstore(0x40, 0x60)
    tag_1:
      0x0
      dup1
      revert

auxdata: 0xa165627a7a72305820af3193f6fd31031a0e0d2de1ad2c27352b1ce081b4f3c92b5650ca4dd542bb770029
}

Binary:
60606040523415600e57600080fd5b5b60016000819055505b5b60368060266000396000f30060606040525b600080fd00a165627a7a72305820af3193f6fd31031a0e0d2de1ad2c27352b1ce081b4f3c92b5650ca4dd542bb770029

The number 6060604052... is bytecode that the EVM actually runs.
最后生成的6060604052...便是EVM實(shí)際運(yùn)行的字節(jié)碼

In Baby Steps

Half of the compiled assembly is boilerplate that’s similar across most Solidity programs. We’ll look at those later. For now, let’s examine the unique part of our contract, the humble storage variable assignment:

一步一步分析

上面編譯生成的匯編代碼有一半都是大部分Solidity程序固定的框架,所以我們只需要關(guān)注我們合約中獨(dú)特的部分,即對(duì)存儲(chǔ)變量賦值的那部分。

a = 1

This assignment is represented by the bytecode 6001600081905550. Let’s break it up into one instruction per line:
該賦值語(yǔ)句轉(zhuǎn)化成字節(jié)碼后是6001600081905550。將其按指令分行展示

60 01
60 00
81
90
55
50

The EVM is basically a loop that execute each instruction from top to bottom. Let’s annotate the assembly code (indented under the label tag_2) with the corresponding bytecode to better see how they are associated:

EVM從上倒下依次執(zhí)行每條指令。讓我們將tag2以下代碼與其對(duì)應(yīng)的助記符聯(lián)系起來(lái)看:

tag_2:
  // 60 01
  0x1
  // 60 00
  0x0
  // 81
  dup2
  // 90
  swap1
  // 55
  sstore
  // 50
  pop

Note that 0x1 in the assembly code is actually a shorthand for push(0x1). This instruction pushes the number 1 onto the stack.

It still hard to grok what’s going on just staring at it. Don’t worry though, it’s simple to simulate the EVM line by line.

注意: 0x1是push(0x1)的簡(jiǎn)化形式,它將數(shù)字1壓棧。
到目前為止依舊不是很清楚,別擔(dān)心!走讀EVM字節(jié)碼并沒(méi)有想象中的那么困難。

Simulating The EVM

The EVM is a stack machine. Instructions might use values on the stack as arguments, and push values onto the stack as results. Let’s consider the operation add.

EVM是基于棧的機(jī)器,指令讀取棧上元素的值作為輸入,并將運(yùn)算結(jié)果壓棧。以add指令為例:

Assume that there are two values on the stack:
假設(shè)現(xiàn)在棧上已經(jīng)有了兩個(gè)元素如下:

[1 2]

When the EVM sees add, it adds the top 2 items together, and pushes the answer back onto the stack, resulting in:

當(dāng)EVM運(yùn)行到add指令時(shí),它將棧頂兩個(gè)元素彈出,將其相加后的記過(guò)壓棧,運(yùn)算之后的棧變成了:

[3]

In what follows, we’ll notate the stack with []:
下文都以[]表示EVM運(yùn)行過(guò)程中棧的狀態(tài)

// The empty stack
// 空棧
stack: []
// Stack with three items. The top item is 3. The bottom item is 1.
//一個(gè)包含3個(gè)元素的棧,棧頂元素是3,棧底元素是1.
stack: [3 2 1]

And notate the contract storage with {}:
另外,使用{}表示EVM運(yùn)行時(shí)存儲(chǔ)器的狀態(tài):

// Nothing in storage.
// 空的存儲(chǔ)器
store: {}
// The value 0x1 is stored at the position 0x0.
//在0x0位置包含一個(gè)值為0x1的元素
store: { 0x0 => 0x1 }

譯注:在以太坊源碼中,數(shù)據(jù)結(jié)構(gòu)Stack表示棧,Memory表示存儲(chǔ)器。

Let’s now look at some real bytecode. We’ll simulate the bytecode sequence 6001600081905550 as EVM would, and print out the machine state after each instruction:

下面我們來(lái)看真實(shí)的字節(jié)碼。我們會(huì)模擬EVM執(zhí)行字節(jié)碼 6001600081905550并標(biāo)識(shí)出每一步后機(jī)器的狀態(tài)

// 60 01: pushes 1 onto stack
// 60 01: 將 1 壓棧
0x1
  stack: [0x1]

// 60 00: pushes 0 onto stack
// 60 01: 將 0 壓棧
0x0
  stack: [0x0 0x1]

// 81: duplicate the second item on the stack
// 81: 將棧頂往下第2個(gè)元素復(fù)制一次放到棧頂
dup2
  stack: [0x1 0x0 0x1]

// 90: swap the top two items
// 90: 交換棧頂兩個(gè)元素
swap1
  stack: [0x0 0x1 0x1]

// 55: store the value 0x1 at position 0x0
// This instruction consumes the top 2 items
// 55:將 0x1 保存在 0x0
// 這條指令彈出棧頂前2個(gè)元素
sstore
  stack: [0x1]
  store: { 0x0 => 0x1 }

// 50: pop (throw away the top item)
// 50: 彈出棧頂元素
pop
  stack: []
  store: { 0x0 => 0x1 }

The end. The stack is empty, and there’s one item in storage.
最終,??樟?。存儲(chǔ)器中包含一個(gè)元素

What’s worth noting is that Solidity had decided to store the state variable uint256 a at the position 0x0. It"s perfectly possible for other languages to choose to store the state variable elsewhere.

值得注意的是Solidity已經(jīng)會(huì)將uint256 a固定在0x0位置,在其他高級(jí)語(yǔ)言中,我們可以主動(dòng)指定其存儲(chǔ)位置。

In pseudocode, what the EVM does for 6001600081905550 is essentially:
偽代碼表示6001600081905550就是

// a = 1
sstore(0x0, 0x1)

Looking carefully, you’d see that the dup2, swap1, pop are superfluous. The assembly code could be simpler:
仔細(xì)觀察,你會(huì)發(fā)現(xiàn)諸如dup2, swap1, pop這些指令都是多余的。匯編代碼像下面這樣就足夠了

0x1
0x0
sstore

You could try to simulate the above 3 instructions, and satisfy yourself that they indeed result in the same machine state:
模擬執(zhí)行以上三條指令,你會(huì)發(fā)現(xiàn)和之前的那種方式相比,最后的結(jié)果是一樣的。

stack: []
store: { 0x0 => 0x1 }
Two Storage Variables 兩個(gè)存儲(chǔ)變量

Let’s add one extra storage variable of the same type:
在之前的例子的基礎(chǔ)上增加一個(gè)相同類(lèi)型的變量

// c2.sol
pragma solidity ^0.4.11;
contract C { uint256 a; uint256 b; function C() { a = 1; b = 2; } }

Compile, focusing on tag_2:
編譯后,僅關(guān)注tag_2

$ solc --bin --asm c2.sol

// ... more stuff omitted
tag_2:
    /* "c2.sol":99:100  1 */
  0x1
    /* "c2.sol":95:96  a */
  0x0
    /* "c2.sol":95:100  a = 1 */
  dup2
  swap1
  sstore
  pop
    /* "c2.sol":112:113  2 */
  0x2
    /* "c2.sol":108:109  b */
  0x1
    /* "c2.sol":108:113  b = 2 */
  dup2
  swap1
  sstore
  pop

The assembly in pseudocode:
匯編的偽碼為:

// a = 1
sstore(0x0, 0x1)
// b = 2
sstore(0x1, 0x2)

What we learn here is that the two storage variables are positioned one after the other, with a in position 0x0 and b in position 0x1.
可以看到,兩個(gè)變量以此存儲(chǔ)在存儲(chǔ)器中,a 0x0 b 0x1.

Storage Packing 存儲(chǔ)空間壓縮

Each slot storage can store 32 bytes. It’d be wasteful to use all 32 bytes if a variable only needs 16 bytes. Solidity optimizes for storage efficiency by packing two smaller data types into one storage slot if possible.

(存儲(chǔ)器由很多個(gè)存儲(chǔ)槽組成,)每個(gè)存儲(chǔ)槽可以存放32字節(jié)的數(shù)據(jù)。如果一個(gè)變量只需要16字節(jié)的存儲(chǔ)空間,但卻讓它占用一個(gè)完整的32字節(jié)空間,顯然很浪費(fèi)的。因此Solidity編譯器會(huì)近可能地將兩個(gè)小的數(shù)據(jù)類(lèi)型放到一個(gè)存儲(chǔ)槽中。

Let’s change a and b so they are only 16 bytes each:
將上面例子中的a b定義成16字節(jié)

pragma solidity ^0.4.11;

contract C {
    uint128 a;
    uint128 b;

function C() {
      a = 1;
      b = 2;
    }
}

Compile the contract:
編譯合約

$ solc --bin --asm c3.sol

The generated assembly is now more complex:

tag_2:
  // a = 1
 0x1
stack: [0x1]
  0x0
stack: [0x1,0x1]
  dup1
stack: [0x0,0x0,0x1]
  0x100
stack: [0x100,0x0,0x0,0x1]
  exp
stack: [0x1,0x0,0x1]
  dup2
stack: [0x0,0x1,0x0,0x1]
  sload
stack: [0x0,0x1,0x0,0x1]
  dup2
stack: [0x1,0x0,0x1,0x0,0x1]
  0xffffffffffffffffffffffffffffffff
stack: [0xff..ff,0x1,0x0,0x1,0x0,0x1]
  mul
stack: [0xff..ff,0x0,0x1,0x0,0x1]
  not
stack: [0x0,0x0,0x1,0x0,0x1]
  and
stack: [0x0,0x1,0x0,0x1]
  swap1
stack: [0x1,0x0,0x0,0x1]
  dup4
stack: [0x1,0x1,0x0,0x0,0x1]
  0xffffffffffffffffffffffffffffffff
stack: [0xff..ff,0x1,0x1,0x0,0x0,0x1]
  and
stack: [0x1,0x1,0x0,0x0,0x1]
  mul
stack: [0x1,0x0,0x0,0x1]
  or
stack: [0x1,0x0,0x1] 
 swap1
stack: [0x0,0x1,0x1]
  sstore
stack: [0x1]
storage:{0x0 => 0x1}
  pop
stack: [0x0]
------------------------------------------------------------------------
  0x2
stack: [0x2]
  0x0
stack: [0x0,0x2]
  0x10
stack: [0x10,0x0,0x2]
  0x100
stack: [0x100,0x10,0x0,0x2]
  exp
stack: [0x100..00,0x0,0x2]
  dup2
stack: [0x0,0x100..00,0x0,0x2]
  sload
stack: [0x1,0x100..00,0x0,0x2]
  dup2
stack: [0x100..00,0x1,0x100..00,0x0,0x2]
  0xffffffffffffffffffffffffffffffff
stack: [0xff..ff,0x100..00,0x1,0x100..00,0x0,0x2]
  mul
stack: [0xff..ff00..00,0x1,0x100..00,0x0,0x2]
  not
stack: [0x00..00ff..ff,0x1,0x100..00,0x0,0x2]
  and
stack: [0x1,0x100..00,0x0,0x2]
  swap1
stack: [0x100..00,0x1,0x0,0x2]
  dup4
stack: [0x2,0x100..00,0x1,0x0,0x2]
  0xffffffffffffffffffffffffffffffff
stack: [0xff..ff,2,0x100..00,1,0,2]
  and
stack: [0x2,0x100..00,0x1,0x0,0x2]
  mul
stack: [0x200..00,0x1,0x0,0x2]
  or
stack: [0x200..01,0x0,0x2]
  swap1
stack: [0x0,0x200..01,0x2]
  sstore
stack: [0x2]
{0x0 => 0x200..01}
  pop
stack: []

The above assembly code packs these two variables together in one storage position (0x0), like this:
上面的匯編碼最終讓兩個(gè)變量存儲(chǔ)在相同的位置(0x0)

[         b         ][         a         ]
[16 bytes / 128 bits][16 bytes / 128 bits]

譯注:我將每一步執(zhí)行之后的棧的狀態(tài)也顯示了出來(lái),盡管結(jié)果符合預(yù)期,但我也不明白為什么感覺(jué)繞了很大一圈。

The reason to pack is because the most expensive operations by far are storage usage:

sstore costs 20000 gas for first write to a new position.

sstore costs 5000 gas for subsequent writes to an existing position.

sload costs 500 gas.

Most instructions costs 3~10 gases.

By using the same storage position, Solidity pays 5000 for the second store variable instead of 20000, saving us 15000 in gas.

將變量壓縮存儲(chǔ)在一起的原因是在區(qū)塊鏈中存儲(chǔ)操作是到目前為止最昂貴的操作了:

sstore 在一個(gè)新的位置存儲(chǔ)要花費(fèi)20000 gas。

sstore 在一個(gè)舊的位置存儲(chǔ)要花費(fèi)5000 gas。

sload 從一個(gè)位置讀取花費(fèi)500 gas。

大多數(shù)指令花費(fèi) 3~10 gas

譯注:存儲(chǔ)很貴!存儲(chǔ)很貴!存儲(chǔ)很貴。每條指令的花費(fèi)在 corevmjumptable.go中指令表的gasCost函數(shù)獲取

More Optimization 更多的優(yōu)化

Instead of storing a and b with two separate sstore instructions, it should be possible to pack the two 128 bits numbers together in memory, then store them using just one sstore, saving an additional 5000 gas.

You can ask Solidity to make this optimization by turning on the optimize flag:
上面的例子中,為了存儲(chǔ)ab兩個(gè)變量,我們使用了兩次 sstore 指令。其實(shí)完全可以先將這兩個(gè)128比特變量在內(nèi)存中就打包成一個(gè)變量,然后調(diào)用一次 sstore 指令,這樣足足可以省下5000 gas。

$ solc --bin --asm --optimize c3.sol

Which produces assembly code that uses just one sload and one sstore:
(顯式地使用--optimize選項(xiàng))生成的匯編碼只使用了一次sload sstore

tag_2:
    /* "c3.sol":95:96  a */
  0x0
    /* "c3.sol":95:100  a = 1 */
  dup1
  sload
    /* "c3.sol":108:113  b = 2 */
  0x200000000000000000000000000000000
  not(sub(exp(0x2, 0x80), 0x1))
    /* "c3.sol":95:100  a = 1 */
  swap1
  swap2
  and
    /* "c3.sol":99:100  1 */
  0x1
    /* "c3.sol":95:100  a = 1 */
  or
  sub(exp(0x2, 0x80), 0x1)
    /* "c3.sol":108:113  b = 2 */
  and
  or
  swap1
  sstore

The bytecode is:
最終生成的字節(jié)碼為

600080547002000000000000000000000000000000006001608060020a03199091166001176001608060020a0316179055

And formatting the bytecode to one instruction per line:
將字節(jié)碼按指令逐條顯示

譯注:同樣,我將其每一步的棧的狀態(tài)顯示出來(lái)

// push 0x0
60 00
stack: [0x0]
// dup1
80
stack: [0x0,0x0]
// sload
54
stack: [0x0,0x0]
// push17 push the the next 17 bytes as a 32 bytes number
70 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
stack: [0x200..00,0x0,0x0]

/* not(sub(exp(0x2, 0x80), 0x1)) */
// push 0x1
60 01
stack: [0x1,0x200000000000000000000000000000000,0x0,0x0]
// push 0x80 (32)
60 80
stack: [0x80,0x1,0x200000000000000000000000000000000,0x0,0x0]
// push 0x02 (2)
60 02
stack: [0x02,0x80,0x1,0x200000000000000000000000000000000,0x0,0x0]
// exp
0a
stack: [0x100000000000000000000000000000000,0x1,0x200000000000000000000000000000000,0x0,0x0]
// sub
03
stack: [0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,0x200000000000000000000000000000000,0x0,0x0]
// not
19
stack: [0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000,0x200000000000000000000000000000000,0x0,0x0]
// swap1
90
stack: [0x200000000000000000000000000000000,0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000,0x0,0x0]
// swap2
91
stack: [0x0,0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000,0x200000000000000000000000000000000,0x0]
// and
16
stack: [0x0,0x200000000000000000000000000000000,0x0]
// push 0x1
60 01
stack: [0x1, 0x0,0x200000000000000000000000000000000,0x0]
// or
17
stack: [0x1,0x200000000000000000000000000000000,0x0]

/* sub(exp(0x2, 0x80), 0x1) */
// push 0x1
60 01
stack: [0x1,0x1,0x200000000000000000000000000000000,0x0]
// push 0x80
60 80
stack: [0x80,0x1,0x1,0x200000000000000000000000000000000,0x0]
// push 0x02
60 02
stack: [0x2,0x80,0x1,0x1,0x200000000000000000000000000000000,0x0]
// exp
0a
stack: [0x100000000000000000000000000000000,0x1,0x1,0x200000000000000000000000000000000,0x0]
// sub
03
stack: [0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF,0x1,0x200000000000000000000000000000000,0x0]
// and
16
stack: [0x1,0x200000000000000000000000000000000,0x0]
// or
17
stack: [0x200000000000000000000000000000001,0x0]
// swap1
90
stack: [0x0,0x200000000000000000000000000000001]
// sstore
55
stack: []
storeage:{0x0 => 0x200..01}

There are four magic values used in the assembly code:
上面的匯編代碼中出現(xiàn)了4個(gè)幻數(shù)(常數(shù))

0x1 (16 bytes), using lower 16 bytes

0x1 (16 字節(jié)), 存放在低16字節(jié)

// Represented as 0x01 in bytecode
16:32 0x00000000000000000000000000000000
00:16 0x00000000000000000000000000000001

0x2 (16 bytes), using higher 16bytes

0x2 (16 字節(jié)), 存放在高16字節(jié)

// Represented as 0x200000000000000000000000000000000 in bytecode
16:32 0x00000000000000000000000000000002
00:16 0x00000000000000000000000000000000

not(sub(exp(0x2, 0x80), 0x1))

// Bitmask for the upper 16 bytes
16:32 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
00:16 0x00000000000000000000000000000000

sub(exp(0x2, 0x80), 0x1)

// Bitmask for the lower 16 bytes
16:32 0x00000000000000000000000000000000 
00:16 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

The code does some bits-shuffling with these values to arrive at the desired result:
最終通過(guò)位運(yùn)算組合成最終的結(jié)果

16:32 0x00000000000000000000000000000002 
00:16 0x00000000000000000000000000000001

Finally, this 32bytes value is stored at position 0x0.
最后再把32字節(jié)的結(jié)果存儲(chǔ)在位置0x0

Gas Usage Gas 使用量
600080547002000000000000000000000000000000006001608060020a03199091166001176001608060020a0316179055

Notice that 0x200000000000000000000000000000000 is embedded in the bytecode. But the compiler could’ve also chosen to calculate the value with the instructions exp(0x2, 0x81), which results in shorter bytecode sequence.
注意,在前面的例子中,我們將0x200000000000000000000000000000000 直接嵌入在了最終的字節(jié)碼中。編譯器本可以通過(guò)exp(0x2, 0x81)得到相同的結(jié)果,顯然后者的字節(jié)碼要短一些。

But it turns out that 0x200000000000000000000000000000000 is a cheaper than exp(0x2, 0x81). Let"s look at the gas fees involved:

4 gas paid for every zero byte of data or code for a transaction.

68 gas for every non-zero byte of data or code for a transaction.

但實(shí)際上,用0x200000000000000000000000000000000 的方式更節(jié)省gas,我們可以計(jì)算下

字節(jié)碼中的每個(gè)值為0的字節(jié)花費(fèi)是 4 gas.

字節(jié)碼中的每個(gè)值為非0的字節(jié)花費(fèi)是 68 gas.

Let’s compare how much either representation costs in gas.

The bytecode 0x200000000000000000000000000000000. It has many zeroes, which are cheap.

于是,使用 0x200000000000000000000000000000000的方式,得益于它包含大量的0,于是實(shí)際上它更便宜。

(1 68) + (16 4) = 196.

The bytecode 608160020a. Shorter, but no zeroes.

相比之下,608160020a更短,但由于沒(méi)有0,實(shí)際會(huì)消耗更多的gas

5 * 68 = 340.

The longer sequence with more zeroes is actually cheaper!
結(jié)論就是:擁有更多的0的長(zhǎng)字節(jié)碼序列更加便宜

Summary

An EVM compiler doesn’t exactly optimize for bytecode size or speed or memory efficiency. Instead, it optimizes for gas usage, which is an layer of indirection that incentivizes the sort of calculation that the Ethereum blockchain can do efficiently.

We’ve seen some quirky aspects of the EVM:

EVM is a 256bit machine. It is most natural to manipulate data in chunks of 32 bytes.

Persistent storage is quite expensive.

The Solidity compiler makes interesting choices in order to minimize gas usage.

Gas costs are set somewhat arbitrarily, and could well change in the future. As costs change, compilers would make different choices.

總結(jié)

EVM編譯器并不會(huì)為了執(zhí)行速度和內(nèi)存效率優(yōu)化代碼,取而代之的是,它會(huì)將代碼優(yōu)化地使用更少的gas。

我們從之前的例子可以看出EVM的一些特性

EVM 是256比特位寬的機(jī)器,可以天然處理32字節(jié)寬度的數(shù)據(jù)

存儲(chǔ)是非常昂貴的操作

Solidity編譯器盡可能地優(yōu)化代碼gas的使用量

Gas的計(jì)算方式看上去有些武斷,也許在以后計(jì)算方式會(huì)改變。如果指令的花費(fèi)價(jià)格發(fā)生變化,編譯器也會(huì)相應(yīng)改變生成的代碼。

*

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/24407.html

相關(guān)文章

  • 以太是什么 - 以太開(kāi)發(fā)入門(mén)指南

    摘要:以太坊是什么以太坊是一個(gè)建立在區(qū)塊鏈技術(shù)之上,去中心化應(yīng)用平臺(tái)。運(yùn)行環(huán)境以太坊虛擬機(jī)是以太坊中智能合約的運(yùn)行環(huán)境。是由以太坊客戶(hù)端提供的是典型的開(kāi)發(fā)以太坊時(shí)使用的客戶(hù)端,基于語(yǔ)言開(kāi)發(fā)。 本文首發(fā)于深入淺出區(qū)塊鏈社區(qū)原文鏈接:以太坊是什么 - 以太坊開(kāi)發(fā)入門(mén)指南原文已更新,請(qǐng)讀者前往原文閱讀 很多同學(xué)已經(jīng)躍躍欲試投入到區(qū)塊鏈開(kāi)發(fā)隊(duì)伍當(dāng)中來(lái),可是又感覺(jué)無(wú)從下手,本文將基于以太坊平臺(tái),以通俗...

    tomlingtm 評(píng)論0 收藏0
  • 以太智能合約開(kāi)發(fā)第二篇:理解以太相關(guān)概念

    摘要:原文發(fā)表于以太坊智能合約開(kāi)發(fā)第二篇理解以太坊相關(guān)概念很多人都說(shuō)比特幣是區(qū)塊鏈,以太坊是區(qū)塊鏈。它是以太坊智能合約的運(yùn)行環(huán)境。是由以太坊節(jié)點(diǎn)提供。以太坊社區(qū)把基于智能合約的應(yīng)用稱(chēng)為去中心化的應(yīng)用。 原文發(fā)表于:以太坊智能合約開(kāi)發(fā)第二篇:理解以太坊相關(guān)概念 很多人都說(shuō)比特幣是區(qū)塊鏈1.0,以太坊是區(qū)塊鏈2.0。在以太坊平臺(tái)上,可以開(kāi)發(fā)各種各樣的去中心化應(yīng)用,這些應(yīng)用構(gòu)成了以太坊的整個(gè)生態(tài)...

    yibinnn 評(píng)論0 收藏0
  • SegmentFault 技術(shù)周刊 Vol.41 - 深入學(xué)習(xí)區(qū)塊鏈

    摘要:和比特幣協(xié)議有所不同的是,以太坊的設(shè)計(jì)十分靈活,極具適應(yīng)性。超級(jí)賬本區(qū)塊鏈的商業(yè)應(yīng)用超級(jí)賬本超級(jí)賬本是基金會(huì)下的眾多項(xiàng)目中的一個(gè)。證書(shū)頒發(fā)機(jī)構(gòu)負(fù)責(zé)簽發(fā)撤 showImg(https://segmentfault.com/img/bV2ge9?w=900&h=385); 從比特幣開(kāi)始 一個(gè)故事告訴你比特幣的原理及運(yùn)作機(jī)制 這篇文章的定位會(huì)比較科普,盡量用類(lèi)比的方法將比特幣的基本原理講出來(lái)...

    qianfeng 評(píng)論0 收藏0
  • 開(kāi)源區(qū)塊鏈Tendermint開(kāi)發(fā)詳解

    摘要:課程概述本課程適合希望開(kāi)發(fā)自己的專(zhuān)有區(qū)塊鏈的語(yǔ)言工程師,課程內(nèi)容如下第一章課程簡(jiǎn)介簡(jiǎn)單介紹的定位特點(diǎn)以及對(duì)于開(kāi)發(fā)者而言與以太坊的區(qū)別。課程地址區(qū)塊鏈開(kāi)發(fā)詳解 簡(jiǎn)介 tendermint是一個(gè)開(kāi)源的完整的區(qū)塊鏈實(shí)現(xiàn),可以用于公鏈或聯(lián)盟鏈,其官方定位 是面向開(kāi)發(fā)者的區(qū)塊鏈共識(shí)引擎: showImg(https://segmentfault.com/img/remote/1460000016...

    wenshi11019 評(píng)論0 收藏0
  • 區(qū)塊鏈開(kāi)發(fā)中使用的最流行的編程語(yǔ)言

    摘要:我們目前正處于一個(gè)新興的區(qū)塊鏈開(kāi)發(fā)行業(yè)中。,一種在以太坊開(kāi)發(fā)人員中流行的新的簡(jiǎn)單編程語(yǔ)言,因?yàn)樗怯糜陂_(kāi)發(fā)以太坊智能合約的語(yǔ)言。它是全球至少萬(wàn)開(kāi)發(fā)人員使用的世界上最流行的編程語(yǔ)言之一。以太坊,主要是針對(duì)工程師使用進(jìn)行區(qū)塊鏈以太坊開(kāi)發(fā)的詳解。 我們目前正處于一個(gè)新興的區(qū)塊鏈開(kāi)發(fā)行業(yè)中。區(qū)塊鏈技術(shù)處于初期階段,然而這種顛覆性技術(shù)已經(jīng)成功地風(fēng)靡全球,并且最近經(jīng)歷了一場(chǎng)與眾不同的繁榮。由于許多...

    2shou 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<