以太坊虛擬機(EVM)
- EVM是智能合約的運行環境
- 作為區塊驗證協議的一部分,參與網絡的每個節點都會運行EVM,審查節點會檢查驗證正在驗證的區塊中列出的交易,并運行EVM中交易觸發的代碼
- EVM是沙盒封裝的,并且是完全隔離的,即EVM中運行的代碼是無法訪問網絡、文件系統和其他的進程,甚至合約之間的訪問也是相互受到限制
- 合約以字節碼格式存在于區塊鏈上
- 合約通常以高級語言(solidity)編寫,通過EVM編譯器編譯為字節碼,最終通過客戶端部署到區塊鏈網絡中
EVM和賬戶
- 以太坊中有兩類賬戶:外部賬戶和合約賬戶,他們共用EVM中同一個地址空間
- 無論賬戶是否存儲代碼,兩類賬戶對于EVM來說處理方式是一樣的
- 每個賬戶在EVM中都有一個鍵值隊形式的持久化存儲。其中key和value的長度都是256位,稱之為存儲空間(storage)
EVM和交易
- 交易是指一個賬戶發送到另一個賬戶的消息,消息包含二進制數據(payload)和以太幣
- 如果目標賬戶含有代碼,這個代碼就會在EVM中執行,并以payload作為入參,這就是合約的調用
- 如果目標賬戶是零賬戶(賬戶地址是0x000。。。),這個交易就會創建一個一個新合約,這個用來創建合約的交易的payload會被轉換為EVM字節碼并執行,執行的輸出作為合約代碼永久存儲
EVM和gas
- 合約被交易觸發調用時,指令會在全網的每個節點上執行,這需要消耗算力成本,每一個指令的執行都會有特定的消耗,gas是用來量化表示這個成本的消耗
- 一經創建,每一筆交易都會按照一定數量的gas預支付一筆費用,目的是限制交易所需要的工作量和為交易支付手續費
- EVM執行交易的時候,gas會按照特定的規則逐漸耗盡
- gas price是交易發送者設置的一個數值,作為發送者預先支付的手續費的單價,如果交易完成之后,gas還有剩余,會原路返回
- 無論執行到什么位置,一旦gas被耗盡(比如降為負值)將會觸發out-of-gas的異常,當前調用幀(call frame)所做的所有狀態修改就會回滾
EVM數據存儲
storage
- 每一個賬戶都會有一個持久化的存儲空間,稱之為storage,這是一個將256位字映射到256位的key-value存儲區,可以理解為合約的數據庫
- 永久存儲在區塊鏈中,由于永久保存合約狀態變量,因此讀寫的gas開銷也是最大的
Memory(內存)
- 每一次消息的調用,合約會臨時獲取一塊干凈的內存空間
- 生命周期僅為整個方法的執行期間,函數的調用回收,因為僅僅保存臨時變量,所以gas的開銷較小
Stack(棧)
- EVM不是基于寄存器的,而是基于棧的,因此所有的計算都是在一個被稱為棧(stack)的區域執行
- 存放部分局部值類型的變量,幾乎免費使用的內存,但是會有數量的限制
EVM指令集
- 所有指令都是針對“256位的字”這個基本的數據類型來進行操作
- 具備常用的算數、位、邏輯和比較操作,也可以做到有條件和無條件跳轉
- 合約可以訪問當前區塊的相關屬性,比如區塊的高度和時間戳
消息調用(Massage calls)
- 合約可以通過消息調用的方式來調用其它合約或者發送以太幣到非合約賬戶
- 合約可以決定在其內部的消息調用中,對于剩余的gas,應該發送和保留多少
- 如果在內部消息調用時發生了out-of-gas異常(或者其它任何的異常),這將由一個被壓入棧頂的錯誤值所指明;此時,只有與該內部消息調用一起發送的gas會被消耗掉
委托調用(Delegatecall)
- 一種特殊類型的消息調用
- 目標地址的代碼將會在發起調用的合約的上下文中執行,并且msg.sender和msg.value不會變
- 可以由實現“庫”(library):可以反復使用的代碼庫放到一個合約的存儲上,通過委托調用引入相應代碼
合約的創建和自毀
- 通過一個特殊的消息調用create calls,合約可以創建其它合約(不是簡簡單單的調用零地址)
- 合約代碼從區塊鏈上移除的唯一的方式就是合約在合約地址上執行自毀操作selfdestruct;合約賬戶剩余的以太幣會發送指定的目標,然后其存儲和代碼都會從狀態中被移除