兄弟連Go語言+區塊鏈培訓學院院長尹成資深區塊鏈技術專家:畢業於清華大學,曾擔任Google演算法工程師,微軟區塊鏈領域全球最具價值專家,微軟Tech.Ed大會金牌講師。精通C/C++、Python、Go語言、Sicikit-Learn與TensorFlow。擁有15年編程經驗與5年的教學經驗,資深軟體架構師,Intel軟體技術專家,著名技術專家,具備多年的世界頂尖IT公司微軟Google的工作經驗。具備多年的軟體編程經驗與講師授課經曆,並在人機互動、教育、資訊安全、廣告、區塊鏈系統開發諸多產品。具備深厚的專案管理經驗以及研發經驗,擁有兩項人工智慧發明專利,與開發電子貨幣部署到微軟WindowsAzure的實戰經驗。教學講解深入淺出,使學員能夠做到學以致用。
#以太坊交易執行分析
在這裡,將其整體串起來,從state_processor.Process函數開始,歸納一下其所作的處理。
##1 Process
Process 根據以太坊規則運行交易資訊來對statedb進行狀態改變,以及獎勵挖礦者或者是其他的叔父節點。
Process返回執行過程中累計的收據和日誌,並返回過程中使用的Gas。 如果由於Gas不足而導致任何交易執行失敗,將返回錯誤。
**處理邏輯:**
~~~
1. 定義及初始化收據、耗費的gas、區塊頭、日誌、gas池等變數;
2. 如果是DAO事件硬分叉相關的處理,則調用misc.ApplyDAOHardFork(statedb)執行處理;
3. 對區塊中的每一個交易,進行迭代處理;處理邏輯:
a. 對當前交易做預先處理,設定交易的hash、索引、區塊hash,供EVM發布新的狀態日誌使用;
b. 執行ApplyTransaction,擷取收據;
c. 若上一步出錯,中斷整個Process,返回錯誤;
d. 若正常,累積記錄收據及日誌。迴圈進入下一個交易的處理。
4. 調用共識模組做Finalize處理;
5. 返回所有的收據、日誌、總共使用的gas。
~~~
##2 ApplyTransaction(1.3.b )
ApplyTransaction嘗試將交易應用於給定的狀態資料庫,並使用輸入參數作為其環境。
它返回交易的收據,使用的Gas和錯誤,如果交易失敗,表明塊是無效的。
**處理邏輯:**
~~~
1. 將types.Transaction結構變數轉為core.Message對象;這過程中會對寄件者做簽名驗證,並獲得寄件者的地址緩衝起來;
2. 建立新的上下文(Context),此上下文將在EVM 環境(EVM environment)中使用;上下文中包含msg,區塊頭、區塊指標、作者(挖礦者、獲益者);
3. 建立新的EVM environment,其中包括了交易相關的所有資訊以及調用機制;
4. ApplyMessage, 將交易應用於當前的狀態中,也就是執行狀態轉換,新的狀態包含在環境對象中;得到執行結果以及花費的gas;
5. 判斷是否分叉情況( `config.IsByzantium(header.Number)` ),如果不是,擷取當前的statedb的狀態樹根雜湊;
6. 建立一個收據, 用來儲存中間狀態的root, 以及交易使用的gas;
7. 如果是建立合約的交易,那麼我們把建立地址儲存到收據裡面;
8. 拿到所有的日誌並建立日誌的布隆過濾器;返回。
~~~
##3 ApplyMessage(2.4)
ApplyMessage將交易應用於當前的狀態中,代碼裡就是建立了一個StateTransition然後調用其TransitionDb()方法。
ApplyMessage返回由任何EVM執行(如果發生)返回的位元組(但這個傳回值在ApplyTransaction中被忽略了),
使用的Gas(包括Gas退款),如果失敗則返回錯誤。 一個錯誤總是表示一個核心錯誤,
意味著這個訊息對於這個特定的狀態將總是失敗,並且永遠不會在一個塊中被接受。
##4 StateTransition.TransitionDb()
~~~
1. 預檢查,出錯則函數返回;
a. 檢查交易的Nonce值是否合規;
b. buyGas:根據寄件者定的gaslimit和GasPrice,從寄件者餘額中扣除以太幣;從區塊gas池中減掉本次gas;並對運行環境做好更新;
2. 支付固定費用 intrinsic gas;
3. 如果是合約建立, 那麼調用evm的Create方法建立新的合約,使用交易的data作為新合約的部署代碼;
4. 否則不是合約建立,增加寄件者的Nonce值,然後調用evm.Call執行交易;
5. 計算並執行退款,將退回的gas對應的以太幣退回給交易寄件者。
~~~
###4.3 evm.Create建立新的合約
~~~
1. 檢查執行深度,若超過params.CallCreateDepth(即1024)就出錯返回;剛開始的執行深度為0,肯定繼續往下執行;
2. 檢查是否可執行轉賬,即檢查賬戶餘額是否≥要轉賬的數額;
3. 寄件者Nonce加1;
4. 建立合約地址並擷取hash,若該合約地址已存在,或不合法(空),則出錯返回;
5. 儲存statedb快照,然後根據合約地址建立賬戶;
6. 執行轉賬evm.Transfer(在statedb中,將value所代表的以太幣從寄件者賬戶轉到新合約賬戶);
7. 根據寄件者、前面建立的合約賬戶,轉賬的錢,已用的gas建立並初始化合約;將交易的data作為合約的代碼;
8. 運行前一步建立的合約
9. 判斷運行結果是否有錯誤。如果合約成功運行並且沒有錯誤返回,則計算儲存返回資料所需的GAS。 如果由於沒有足夠的GAS而導致傳回值不能被儲存則設定錯誤,並通過下面的錯誤檢查條件來處理。
10. 若EVM返回錯誤或上述儲存傳回值出現錯誤,則復原到快照的狀態,並且消耗完剩下的所有gas。
~~~
###4.4 evm.Call執行交易
Call方法, 無論我們轉賬或者是執行合約代碼都會調用到這裡, 同時合約裡面的call指令也會執行到這裡。
Call方法和evm.Create的邏輯類似,但少了一些步驟。
~~~
1. 檢查是否允許遞迴執行以及執行深度,若深度超過params.CallCreateDepth(即1024)就出錯返回;
2. 檢查是否可執行轉賬,即檢查賬戶餘額是否≥要轉賬的數額;
3. 儲存statedb快照,建立接收者賬戶;
4. 如果接收者在statedb中尚不存在,則執行precompiles先行編譯,與編譯結果為nil時出錯返回;無錯誤則在statedb中建立接收者賬戶;
5. 執行轉賬;
6. 根據寄件者、接收者,轉賬的錢,已用的gas建立並初始化合約;將交易的data作為合約的代碼;
7. 運行前一步建立的合約
8. 若EVM返回錯誤,則復原到快照的狀態,並且消耗完剩下的所有gas。
~~~
虛擬機器中合約的執行另行分析。
### eth源碼交易發送接收,校正儲存分析:
```
建立合約指的是將合約部署到區塊鏈上,這也是通過發送交易來實現。在建立合約的交易中,to欄位要留空不填,在data欄位中指定合約的二進位代碼,
from欄位是交易的寄件者也是合約的建立者。
執行合約的交易
調用合約中的方法,需要將交易的to欄位指定為要調用的合約的地址,通過data欄位指定要調用的方法以及向該方法傳遞的參數。
所有對賬戶的變動操作都會先提交到stateDB裡面,這個類似一個行為資料庫,或者是緩衝,最終執行需要提交到底層的資料庫當中,底層資料庫是levelDB(K,V資料庫)
core/interface.go定義了stateDB的介面
ProtocolManager主要成員包括:
peertSet{}類型成員用來緩衝相鄰個體列表,peer{}表示網路中的一個遠端個體。
通過各種通道(chan)和事件訂閱(subscription)的方式,接收和發送包括交易和區塊在內的資料更新。當然在應用中,訂閱也往往利用通道來實現事件通知。
ProtocolManager用到的這些通道的另一端,可能是其他的個體peer,也可能是系統內單例的資料來源比如txPool,或者是事件訂閱的管理者比如event.Mux。
Fetcher類型成員累積所有其他個體發送來的有關新資料的宣布訊息,並在自身對照後,安排相應的擷取請求。
Downloader類型成員負責所有向相鄰個體主動發起的同步流程。
func(pm *ProtocolManager) Start()
以上這四段相對獨立的商務程序的邏輯分別是:
1.廣播新出現的交易對象。txBroadcastLoop()會在txCh通道的收端持續等待,一旦接收到有關新交易的事件,會立即調用BroadcastTx()函數廣播給那些尚無該交易對象的相鄰個體。
2.廣播新挖掘出的區塊。minedBroadcastLoop()持續等待本個體的新挖掘出區塊事件,然後立即廣播給需要的相鄰個體。當不再訂閱新挖掘區塊事件時,這個函數才會結束等待並返回。很有意思的是,在收到新挖掘出區塊事件後,minedBroadcastLoop()會連續調用兩次BroadcastBlock(),兩次調用僅僅一個bool型參數@propagate不一樣,當該參數為true時,會將整個新區塊依次發給相鄰區塊中的一小部分;而當其為false時,僅僅將新區塊的Hash值和Number發送給所有相鄰列表。
3.定時與相鄰個體進行區塊全鏈的強制同步。syncer()首先啟動fetcher成員,然後進入一個無限迴圈,每次迴圈中都會向相鄰peer列表中“最優”的那個peer作一次區塊全鏈同步。發起上述同步的理由分兩種:如果有新登記(加入)的相鄰個體,則在整個peer列表數目大於5時,發起之;如果沒有新peer到達,則以10s為間隔定時的發起之。這裡所謂"最優"指的是peer中所維護區塊鏈的TotalDifficulty(td)最高,由於Td是全鏈中從創世塊到最新頭塊的Difficulty值總和,所以Td值最高就意味著它的區塊鏈是最新的,跟這樣的peer作區塊全鏈同步,顯然改動量是最小的,此即"最優"。
4.將新出現的交易對象均勻的同步給相鄰個體。txsyncLoop()主體也是一個無限迴圈,它的邏輯稍微複雜一些:首先有一個資料類型txsync{p, txs},包含peer和tx列表;通道txsyncCh用來接收txsync{}對象;txsyncLoop()每次迴圈時,如果從通道txsyncCh中收到新資料,則將它存入一個本地map[]結構,k為peer.ID,v為txsync{},並將這組tx對象發送給這個peer;每次向peer發送tx對象的上限數目100*1024,如果txsync{}對象中有剩餘tx,則該txsync{}對象繼續存入map[]並更新tx數目;如果本次迴圈沒有新到達txsync{},則從map[]結構中隨機找出一個txsync對象,將其中的tx組發送給相應的peer,重複以上迴圈。
以上四段流程就是ProtocolManager向相鄰peer主動發起的通訊過程。儘管上述各函數細節從文字閱讀起來容易模糊,不過最重要的內容還是值得留意下的:本個體(peer)向其他peer主動發起的通訊中,按照資料類型可分兩類:交易tx和區塊block;而按照通訊方式劃分,亦可分為廣播新的單個資料和同步一組同類型資料,這樣簡單的兩兩配對,便可組成上述四段流程。
在上文的介紹中,出現了多處有關p2p通訊協定的結構類型,比如eth.peer,p2p.Peer,Server等等。這裡不妨對這些p2p通訊協定族的結構一併作個總解。以太坊中用到的p2p通訊協定族的結構類型,大致可分為三層:
第一層處於pkg eth中,可以直接被eth.Ethereum,eth.ProtocolManager等頂層管理模組使用,在型別宣告上也明顯考慮了eth.Ethereum的使用特點。典型的有eth.peer{}, eth.peerSet{},其中peerSet是peer的集合類型,而eth.peer代表了遠端通訊對象和其所有通訊操作,它封裝更底層的p2p.Peer對象以及讀寫通道等。
第二層屬於pkg p2p,可認為是泛化的p2p通訊結構,比較典型的結構類型包括代表遠端通訊對象的p2p.Peer{}, 封裝自更底層連線物件的conn{},通訊用通道對象protoRW{}, 以及啟動監聽、處理新加入串連或中斷連線的Server{}。這一層中,各種資料類型的界限比較清晰,盡量不出現揉雜的情況,這也是泛化結構的需求。值得關注的是p2p.Protocol{},它應該是針對上層應用特意開闢的類型,主要作用包括容納應用程式所要求的回呼函數等,並通過p2p.Server{}在新串連建立後,將其傳遞給通訊對象peer。從這個類型所起的作用來看,命名為Protocol還是比較貼切的,儘管不應將其與TCP/IP協議等既有概念混淆。
第三層處於golang內建的網路程式碼封裝中,也可分為兩部分:第一部分pkg net,包括代表網路連接的介面,代表網路地址的以及它們的實作類別;第二部分pkg syscall,包括更底層的網路相關係統調用類等,可視為封裝了網路層(IP)和傳輸層(TCP)協議的系統實現。
```
```
Receiptroot我們剛剛在區塊頭有看到,那他具體包含的是什麼呢?它是一個交易的結果,主要包括了poststate,證券交易所花費的gas,bloom和logs
blockchain無結構化查詢需求,僅hash查詢,key/value資料庫最方便,底層用levelDB儲存,效能好
stateDB用來儲存世界狀態
Core/state/statedb.go
注意:1. StateDB完整記錄Transaction的執行情況; 2. StateDB的重點是StateObjects; 3. StateDB中的 stateObjects,Account的Address為 key,記錄其Balance、nonce、code、codeHash ,以及tire中的 {string:Hash}等資訊;
所有的結構湊明朗了,那具體的驗證過程是怎麼樣的呢
Core/state_processor.go
Core/state_transition.go
Core/block_validator.go
StateProcessor 1. 調用StateTransition,驗證(執行)Transaction; 2. 計算Gas、Recipt、Uncle Reward
StateTransition
1. 驗證(執行)Transaction;
3. 扣除transaction.data.payload計算資料所需要消耗的gas;
4. 在vm中執行code(產生contract or 執行contract);vm執 行過程中,其gas會被自動消耗。如果gas不足,vm會自 選退出;
5. 將多餘的gas退回到sender.balance中;
6. 將消耗的gas換成balance加到當前env.Coinbase()中;
BlockValidator
1. 驗證UsedGas
2. 驗證Bloom
3. 驗證receiptSha
4. 驗證stateDB.IntermediateRoot
/core/vm/evm.go
交易的轉帳操作由Context對象中的TransferFunc類型函數來實現,類似的函數類型,還有CanTransferFunc, 和GetHashFunc。
core/vm/contract.go
合約是evm用來執行指令的結構體
入口:/cmd/geth/main.go/main
```
#EVM分析
>EVM不能被重用,非安全執行緒
Context結構體:為EVM提供輔助資訊。一旦提供,不應更改。
~~~
// Context 為EVM提供輔助資訊。一旦提供,不應更改。
type Context struct {
// CanTransfer 返回 賬戶是否擁有足夠的以太幣以執行轉賬 CanTransfer CanTransferFunc
// Transfer 轉賬函數,將以太幣從一個賬戶轉到另一個賬戶
Transfer TransferFunc
// GetHash 返回n對應的雜湊
GetHash GetHashFunc
// Message information
Origin common.Address // Provides information for ORIGIN
GasPrice *big.Int // Provides information for GASPRICE
// Block information
Coinbase common.Address // Provides information for COINBASE
GasLimit uint64 // Provides information for GASLIMIT
BlockNumber *big.Int // Provides information for NUMBER
Time *big.Int // Provides information for TIME
Difficulty *big.Int // Provides information for DIFFICULTY
}
~~~
> state_processor.Process開始執行交易處理,就是在那裡為入口進入到evm的執行的,具體見[core-state-process-analysis.md](core-state-process-analysis.md)
##EVM的實現
以太坊的EVM整個完全是自己實現的,能夠直接執行Solidity位元組碼,沒有使用任何第三方運行時。
運行過程是同步的,沒有啟用go協程。
1. evm最終是調用Interpreter運行位元組碼;
2. Interpreter.go實現運行處理;解析出作業碼後,通過JumpTable擷取作業碼對應的函數運行,並維護pc計數器、處理傳回值等;
3. jump_table.go定義了作業碼的跳轉映射;
4. instructions.go實現每一個作業碼的具體的處理;
5. opcodes.go中定義了作業碼常量
對於EVM的測試,以太坊將測試代碼放在了core\vm\runtime目錄下,提供了供測試用的運行時及測試案例。
測試案例的樣本如:
~~~
func TestExecute(t *testing.T) {
ret, _, err := Execute([]byte{
byte(vm.PUSH1), 10,
byte(vm.PUSH1), 0,
byte(vm.MSTORE),
byte(vm.PUSH1), 32,
byte(vm.PUSH1), 0,
byte(vm.RETURN),
}, nil, nil)
if err != nil {
t.Fatal("didn't expect error", err)
}
num := new(big.Int).SetBytes(ret)
if num.Cmp(big.NewInt(10)) != 0 {
t.Error("Expected 10, got", num)
}
}
~~~
比特幣沒趕上?以太幣沒有買?你錯過了成為了百萬富翁的夢但是不要錯失成為創造者的機會!!!7月7日起每晚8點-9點半兄弟連區塊鏈學院正式開課:http://www.ydma.cn/open/course/16【清華學霸攜全球區塊鏈大賽冠軍團隊】帶你實戰區塊鏈開發!!!