以太坊虛擬機,簡稱EVM,用來執(zhí)行以太坊上的交易。

EVM 的業(yè)務(wù)流程參見下圖:







輸入一筆交易,內(nèi)部會轉(zhuǎn)換成一個Message對象,傳入EVM執(zhí)行。

如果是一筆普通轉(zhuǎn)賬交易,那么直接修改StateDB中" />

国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)站運營 > 以太坊源碼分析 虛擬機 EVM

以太坊源碼分析 虛擬機 EVM

時間:2023-07-12 15:12:01 | 來源:網(wǎng)站運營

時間:2023-07-12 15:12:01 來源:網(wǎng)站運營

以太坊源碼分析 虛擬機 EVM:

以太坊虛擬機,簡稱EVM,用來執(zhí)行以太坊上的交易。

EVM 的業(yè)務(wù)流程參見下圖:







輸入一筆交易,內(nèi)部會轉(zhuǎn)換成一個Message對象,傳入EVM執(zhí)行。

如果是一筆普通轉(zhuǎn)賬交易,那么直接修改StateDB中對應(yīng)的賬戶余額即可。

如果是智能合約的創(chuàng)建或者調(diào)用,則通過EVM中的解釋器加載和執(zhí)行字節(jié)碼,執(zhí)行過程中可能會查詢或者修改StateDB。

1. 固定油費(Intrinsic Gas)

每筆交易過來,不管三七二十一先需要收取一筆固定油費,計算方法如下:







如果你的交易不帶額外數(shù)據(jù)(Payload),比如普通轉(zhuǎn)賬,那么需要收取21000的油費。

如果你的交易攜帶額外數(shù)據(jù),那么這部分數(shù)據(jù)也是需要收費的,具體來說是按字節(jié)收費:字節(jié)為0的收4塊,字節(jié)不為0收68塊,所以你會看到很多做合約優(yōu)化的,目的就是減少數(shù)據(jù)中不為0的字節(jié)數(shù)量,從而降低油費消耗。

2. 生成Contract對象

交易會被轉(zhuǎn)換成一個Message對象傳入EVM,而EVM則會根據(jù)Message生成一個Contract對象以便后續(xù)執(zhí)行:







可以看到,Contract中會根據(jù)合約地址,從StateDB中加載對應(yīng)的代碼,后面就可以送入解釋器執(zhí)行了。

另外,執(zhí)行合約能夠消耗的油費有一個上限,就是節(jié)點配置的每個區(qū)塊能夠容納的GasLimit。

3.送入解釋器執(zhí)行

代碼跟輸入都有了,就可以送入解釋器執(zhí)行了。EVM是基于棧的虛擬機,解釋器中需要操作四大組件:







具體解釋執(zhí)行的流程參見下圖:





EVM的每條指令稱為一個OpCode,占用一個字節(jié),所以指令集最多不超過256,具體描述參見:https://ethervm.io。比如下圖就是一個示例(PUSH1=0x60, MSTORE=0x52):



首先PC會從合約代碼中讀取一個OpCode,然后從一個JumpTable中檢索出對應(yīng)的operation,也就是與其相關(guān)聯(lián)的函數(shù)集合。接下來會計算該操作需要消耗的油費,如果油費耗光則執(zhí)行失敗,返回ErrOutOfGas錯誤。如果油費充足,則調(diào)用execute()執(zhí)行該指令,根據(jù)指令類型的不同,會分別對Stack、Memory或者StateDB進行讀寫操作。

4. 調(diào)用合約函數(shù)

前面分析完了EVM解釋執(zhí)行的主要流程,可能有些同學(xué)會問:那么EVM怎么知道交易想調(diào)用的是合約里的哪個函數(shù)呢?別急,前面提到跟合約代碼一起送到解釋器里的還有一個Input,而這個Input數(shù)據(jù)是由交易提供的。





Input數(shù)據(jù)通常分為兩個部分:

前面 4 個字節(jié)被稱為“4-byte signature”,是某個函數(shù)簽名的 Keccak 哈希值的前4個字節(jié),作為該函數(shù)的唯一標識。(查詢目前所有的函數(shù)簽名:https://www.4byte.directory

后面跟的就是調(diào)用該函數(shù)需要提供的參數(shù)了,長度不定。

舉個例子:我在部署完A合約后,調(diào)用add(1)對應(yīng)的Input數(shù)據(jù)是0x87db03b70000000000000000000000000000000000000000000000000000000000000001

而在我們編譯智能合約的時候,編譯器會自動在生成的字節(jié)碼的最前面增加一段函數(shù)選擇邏輯:

首先通過CALLDATALOAD指令將“4-byte signature”壓入堆棧中,然后依次跟該合約中包含的函數(shù)進行比對,如果匹配則調(diào)用JUMPI指令跳入該段代碼繼續(xù)執(zhí)行。

這么講可能有點抽象,我們可以看一看上圖中的合約對應(yīng)的反匯編代碼就一目了然了:








這里提到了CALLDATALOAD,就順便講一下數(shù)據(jù)加載相關(guān)的指令,一共有4種:

最后一個EXTCODECOPY不太常用,一般是為了審計第三方合約的字節(jié)碼是否符合規(guī)范,消耗的gas一般也比較多。這些指令對應(yīng)的操作如下圖所示:







5. 合約調(diào)用合約

合約內(nèi)部調(diào)用另外一個合約,有4種調(diào)用方式:

后面會專門寫篇文章比較它們的異同,這里先以最簡單的CALL為例,調(diào)用流程如下圖所示:





可以看到,調(diào)用者把調(diào)用參數(shù)存儲在內(nèi)存中,然后執(zhí)行CALL指令。

CALL指令執(zhí)行時會創(chuàng)建新的Contract對象,并以內(nèi)存中的調(diào)用參數(shù)作為其Input。

解釋器會為新合約的執(zhí)行創(chuàng)建新的Stack和Memory,從而不會破環(huán)原合約的執(zhí)行環(huán)境。

新合約執(zhí)行完成后,通過RETURN指令把執(zhí)行結(jié)果寫入之前指定的內(nèi)存地址,然后原合約繼續(xù)向后執(zhí)行。

6. 創(chuàng)建合約

前面都是討論的合約調(diào)用,那么創(chuàng)建合約的流程時怎么樣的呢?

如果某一筆交易的to地址為nil,則表明該交易是用于創(chuàng)建智能合約的。

首先需要創(chuàng)建合約地址,采用下面的計算公式:Keccak(RLP(call_addr, nonce))[:12]。也就是說,對交易發(fā)起人的地址和nonce進行RLP編碼,再算出Keccak哈希值,取后20個字節(jié)作為該合約的地址。

下一步就是根據(jù)合約地址創(chuàng)建對應(yīng)的stateObject,然后存儲交易中包含的合約代碼。該合約的所有狀態(tài)變化會存儲在一個storage trie中,最終以Key-Value的形式存儲到StateDB中。代碼一經(jīng)存儲則無法改變,而storage trie中的內(nèi)容則是可以通過調(diào)用合約進行修改的,比如通過SSTORE指令。







7.油費計算

最后啰嗦一下油費的計算,計算公式基本上是根據(jù)以太坊黃皮書中的定義:http://gavwood.com/paper.pdf





當(dāng)然你可以直接read the fucking code,代碼位于core/vm/gas.go和core/vm/gas_table.go中。

智能合約開發(fā)、審計點擊聯(lián)系

關(guān)鍵詞:虛擬,分析

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點擊下載Chrome瀏覽器
關(guān)閉