以太坊EVM执行交易过程解释

三个需要理解清楚的概念:

(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。

猜你喜欢

转载自blog.csdn.net/kojhliang/article/details/82870696