处理交易的入口:
整个evm调用的入口在go-ethereum/core/state_transaction.go
中.
ApplyTransaction()
就是执行交易的入口,而交易的执行就离不开EVM
。
每处理一笔交易,就要创建一个 EVM对象 来执行交易中的数据。
处理交易流程:
ApplyTransaction函数:
ApplyTransaction()
和applyTransaction()
函数的主要功能是:将交易转化成Message,创建EVM对象,调用ApplyMessage()
执行交易,生成日志对象;
-
将交易转换成Message
-
初始化一个EVM的执行环境
-
新建 EVM 对象
-
执行交易,改变stateDB世界状态,然后生成收据
1 | func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, error) { |
接着看applyTransaction()
函数
applyTransaction函数:
1 | func applyTransaction(msg types.Message, config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, blockNumber *big.Int, blockHash common.Hash, tx *types.Transaction, usedGas *uint64, evm *vm.EVM) (*types.Receipt, error) { |
接着看ApplyMessage()
函数
ApplyMessage函数:
在ApplyMessage()
中,首先新建一个交易工作环境,然后紧接着调用TransitionDb()
方法:
1 | // ApplyMessage returns the bytes returned by any EVM execution (if it took place)... |
StateTransition.TransitionDb函数:
StateTransition结构体:
交易工作环境(StateTransition)
的数据结构如下:
1 | type StateTransition struct { |
这里gp
是整个区块所有交易可用的gas
,其实就是来自于header
的gaslimit
,
而header
的gasLimit
是通过父区块的gasUsed
推算出来的。
initialGas
是交易的gasLimit
,gas是余额(等于initialGas
减去交易usedGas
)。
TransitionDb函数:
TransitionDb()
的主要功能是 初始化交易工作环境,执行交易,然后处理交易执行前后的gas增减。
-
前置检查:预先检查
nonce
和gas
值,初始化交易工作环境的gas初始值; -
计算并扣除固定gas消耗;
-
通过比较0地址,判断是否是创建新合约,调用evm创建或执行交易;
-
奖励旷工:归还剩余的 gas,并将已消耗的 gas 计入矿工账户中
1 | // TransitionDb will transition the state by applying the current message and |
preCheck函数:
继续分解TransitionDb
里的具体实现在交易执行之前,要buyGas()
之前,要做一些前置检查preCheck()
,主要检查项是:
-
caller的nonce是否正确
-
caller是否有足够的余额来支付交易费(gaslimit + gasprice)
-
区块里剩余可用gas数量
buyGas函数:
前置检查完后,要从区块的gasPool中取出一定的gas用于执行交易,即调用buyGas()
:
-
将initialGas和gas都设置为交易的gasLimit
-
并从总的gaspool里减去预支的gas(即gasLimit)
-
交易发起者的账户也要减去相应的价值。
1 | func (st *StateTransition) buyGas() error { |
gas使用流程:
-
从区块的gasPool中取出一定的gas用于执行交易
buyGas()
-
计算并扣除固定的gas消耗
IntrinsicGas()
(固定的gas消耗(21000) + 非0值gas + 0值gas) -
执行交易,获取剩余的gas
-
归还剩余的 gas
refundGas()
,并将已消耗的 gas 计入矿工账户中
refundGas函数:
1 | func (st *StateTransition) refundGas(refundQuotient uint64) { |