1. 程式人生 > >The future is already here it's just not evenly distributed。

The future is already here it's just not evenly distributed。

     以太坊上交易最終都會由EVM進行解析存入資料庫,今天就來探討一下,一筆交易是如何別EVM執行的。我們可以把交易分為三種。(注意,和交易相關的模組很多,交易的生命週期存在於整個以太坊中,我們這次只是分析和EVM相關的部分。)

     1、以太幣轉移,兩個賬戶之間只發生了以太幣的轉移。

     2、合約建立,使用者建立智慧合約的交易。這類交易的to地址都是空著的。

     3、呼叫合約,使用者去呼叫智慧合約中的某個函式,這類交易中data資訊包含了需要呼叫的函式hash值的前4個位元組,以及引數。例如基於ERC20的Token轉賬就是這種型別,前四個位元組是0xa9059cbb表示transfer(address _to, uint256 _value),後邊跟了兩個引數,to地址和交易數量。

    以太幣轉移

    先看一下以太幣轉移型別的交易,這類交易比較簡單,主要函式是由Context中的三個函式完成的,CanTransfer CanTransferFunc、Transfer TransferFunc。

type Context struct {
	// 賬戶餘額夠不夠
	CanTransfer CanTransferFunc
	// 把eth轉移給另外一個賬戶
	Transfer TransferFunc
	// 對入參n返回一個hash
	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
}

下邊具體分析一下怎麼實現的。


func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool {
	return db.GetBalance(addr).Cmp(amount) >= 0
}

func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
	db.SubBalance(sender, amount)
	db.AddBalance(recipient, amount)
}

    怎麼樣是不是so easy,一眼就看出來了;就是呼叫資料介面,實現資料庫的資料的修改就可以了,對賬戶的餘額進行增加、減少和查詢;當然實際情況並非這麼簡單,之所以看著這麼簡單,就是我們常說的解耦;後續我會繼續分析state和資料庫等模組,那會兒我們就會有更清晰的認識。

合約建立\呼叫合約

合約建立,evm.Create的邏輯和Call方法比較類似的。

evm.call evm.create
檢查棧的深度,剛開始的執行深度為0;超過params.CallCreateDepth(即1024)則返回錯誤;檢查賬戶餘額是否超過要轉賬的數額。

檢查棧的深度是否超過params.CallCreateDepth(即1024);檢查賬戶餘額是否超過要轉賬的數額。

儲存statedb快照;如果to賬戶不存在statedb中,則執行precompiles預編譯,與編譯結果為nil時出錯返回;無錯誤則在statedb中建立to賬戶;

建立者的nonce值加1;建立合約地址並獲取hash;儲存statedb快照,然後根據合約地址建立賬戶;

轉賬Transfer,這一步就是以太幣轉賬的過程。 轉賬Transfer,這一步就是以太幣轉賬的過程。
根據from、to、value、gas建立並初始化合約;獲取to地址的code,呼叫SetCallCode方法,設定合約地址關聯的code; 根據from、to、value、gas建立並初始化合約;獲取入參code,呼叫SetCallCode方法,設定合約地址關聯的code;
debug模式下,棧深度為0,記錄evm執行交易的資訊。 檢查棧的深度是否超過params.CallCreateDepth(即1024);debug模式下,棧深度為0,開始記錄evm執行交易的資訊。
執行之前建立的合約 執行之前建立的合約
出現任何錯誤,statedb回滾到之前快照,消耗剩餘gas。 檢測run結果以及合約大小是否超出限制(out of gas ),出現問題,statedb回滾到之前的快照。
debug模式下,結束記錄evm執行交易的資訊。

call和create幾點不同:

1)由於智慧合約的建立是沒有to地址的,所以SetCallCode方法的引數是從直接有使用者傳進去的的;而call則不一樣,例如你轉賬erc20代幣的時候,to地址是合約地址,所以SetCallCode方法的入參則是從to地址的關聯的code中獲取的。

2)智慧合約建立是一個從無到有的過程,所以statedb中沒有合約的地址和賬戶,所以建立合約地址和賬戶是必然的過程。

3)call()的input型別為[]byte,和create()的code型別同樣為[]byte;這兩者從交易的角度是一樣的;但是當系統解析到to地址時,才有了分化;to地址不為空,則tx.data被當作contract的input,to地址為空的時候,則tx.data被當作contract的code。