三个需要理解清楚的概念:
(1)交易:区块链由区块(Block)组成,而区块中打包一定数量的交易(Transaction),交易分两种:
1.1一个单纯的外部账户之间转账操作
1.2 创建或者调用智能合约的操作
无论是哪一种,EVM在运行(excute)交易时都会创建合约(Contract)。
(2)账户:一笔交易包含发送方(sender) 接收方(recipient) 和数额(value) 三要素。接收方账户分三种情况:
2.1外部账户:发送方将一定数额的ETH转移到接收方的账户,这里的转账交易中,接收方是外部账户。
2.2为空:如果接收账户为空,则为创建合约的操作。
2.3合约账户:而在调用智能合约的交易时,接收方是合约账户。
(3)data 字段:分三种情况:
3.1 普通转账交易,该字段为空
3.2 对创建一个新的合约,该字段为新的合约的代码
3.3 对执行一个已经在区块链上存在的合约,该参数为合约代码的输入参数
源码里执行交易的大致过程
1.core/blockchain.go里InsertChain()—》insertChain()里的
// Process block using the parent state as reference point.
receipts, logs, usedGas, err := bc.processor.Process(block, state, bc.vmConfig)
2.core/state_processor.go里的Process()方法里的
receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg)
3.core/state_processor.go里 ApplyTransaction()方法里执行
// Create a new context to be used in the EVM environment
context := NewEVMContext(msg, header, bc, author)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmenv := vm.NewEVM(context, statedb, config, cfg)
// Apply the transaction to the current state (included in the env)
_, gas, failed, err := ApplyMessage(vmenv, msg, gp)
4.core/stae_transition.go里的ApplyMessage()将执行
return NewStateTransition(evm, msg, gp).TransitionDb()
5. core/stae_transition.go里TransitionDb()方法
msg := st.msg
sender := vm.AccountRef(msg.From())
homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber)
contractCreation := msg.To() == nil
if contractCreation {
ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value)
} else {
// Increment the nonce for the next transaction
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1)
ret, st.gas, vmerr = evm.Call(sender, st.to(), st.data, st.gas, st.value)
}
注意:无论是evm.Create()还是evm.Call()最终都是创建一个Contract结构,然后调用run()方法运行之。
即使是外部账户之间普通的转账也会调用Call() 和 run(),只是由于接收者地址上没有代码,运行会很快结束而已。run()最终调用Interpreter的Run()方法。
6. core/vm/evm.go里的Call()方法
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
//Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
ret, err = run(evm, contract, input)
7.core/vm/evm.go里的run()方法会调用
return interpreter.Run(contract, input)
8.core/vm/interpreter.go里的EVMInterpreter(EVM解释器)的Run方法
func (in *EVMInterpreter) Run(contract *Contract, input []byte) (ret []byte, err error) {
}
核心逻辑为:循环取出合约代码,解析出一个个操作码,然后分别调用对应的处理函数执行。
for atomic.LoadInt32(&in.evm.abort) == 0 {
op = contract.GetOp(pc)
operation := in.cfg.JumpTable[op]
// execute the operation
res, err := operation.execute(&pc, in, contract, mem, stack)
}
注意的两个地方:
(1)调用深度:代表的是智能合约可能调用其他智能合约,这样的深度不能超过1024
// Increment the call depth which is restricted to 1024
in.evm.depth++
defer func() { in.evm.depth-- }()
(2)堆栈深度:EVM虚拟机是基于堆栈式执行的,堆栈大小不能超过1024.
注意:堆栈大小为1024不代表最多只能执行1024条指令,因为堆栈可以push(+1),也可以pop(-1)的。
最终受限的执行操作码所花费的gas。