EVM

EVM源码分析(一)

Posted by Thomas_Xu on 2023-02-20

EVM源码分析(一)


author:Thomas_Xu

源码结构

源码地址:https://github.com/ethereum/go-ethereum

EVM的核心代码在core/vm下,而在进入到EVM之前的处理,我们会在第二章讲到,我们这里只分析EVM的层次结构。

以下为vm的目录结构:

runtime 包下的文件在实际运行的geth客户端中并没有被调用到,只是作为开发人员测试使用。

  • core/vm/runtime/env.go 设置evm运行环境,并返回新的evm对象
  • core/vm/runtime/fuzz.go fuzz使得开发者可以随机测试evm代码,详见go-fuzz工具
  • core/vm/runtime/runtime.go 设置evm运行环境,并执行相应的evm代码

下面的文件为实际的使用到的evm的代码文件

  • core/vm/analysis.go 分析指令跳转目标
  • core/vm/common.go 存放常用工具方法
  • core/vm/contract.go 合约数据结构
  • core/vm/contracts.go 存放预编译好的合约
  • core/vm/errors.go 定义一些常见错误
  • core/vm/evm.go evm对于解释器提供的一些操作接口
  • core/vm/gas.go 计算一级指令耗费gas
  • core/vm/gas_table.go 各种操作的gas消耗计算表
  • core/vm/gen_structlog.go 生成structlog的 序列化json和反序列化方法
  • core/vm/instructions.go 所有指令集的实现函数
  • core/vm/interface.go 定义常用操作接口
  • core/vm/interpreter.go evm 指令解释器
  • core/vm/intpool.go 常量池
  • core/vm/jump_table.go 指令跳转表
  • core/vm/logger.go 状态日志
  • core/vm/logger_json.go json形式日志
  • core/vm/memory.go evm 可操作内存
  • core/vm/memory_table.go evm内存操作表,衡量一些操作耗费内存大小
  • core/vm/opcodes.go 定义操作码的名称和编号
  • core/vm/stacks.go evm栈操作
  • core/vm/stack_table.go evm栈验证函数

contract.go

结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 对合约支持对象的引用
type ContractRef interface {
Address() common.Address
}

// AccountRef implements ContractRef.
//
//帐户引用在 EVM 初始化期间使用,并且它的主要用途是获取地址。删除此对象被证明是困难的,
//因为缓存的跳转目的地从父合约(即调用者)中获取,它是一个 ContractRef。
type AccountRef common.Address

// Address 将 AccountRef 转换为地址
func (ar AccountRef) Address() common.Address { return (common.Address)(ar) }

// 表示状态数据库中的以太坊合约。它包含合约代码,调用参数。合约实现 ContractRef
type Contract struct {
// CallerAddress是初始化这个合约的人。 如果是delegate,这个值被设置为调用者的调用者。
CallerAddress common.Address
caller ContractRef
self ContractRef

jumpdests destinations // result of JUMPDEST analysis. JUMPDEST指令的分析

Code []byte //代码
CodeHash common.Hash //代码的HASH
CodeAddr *common.Address //代码地址
Input []byte // 入参

Gas uint64 // 合约还有多少Gas
value *big.Int

Args []byte

DelegateCall bool
}

构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// NewContract 表示状态数据库中的以太坊合约。它包含合约代码,调用参数。合约实现 ContractRef
func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract {
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil}

if parent, ok := caller.(*Contract); ok {
// Reuse JUMPDEST analysis from parent context if available.
// 如果 caller 是一个合约,说明是合约调用了我们。 jumpdests设置为caller的jumpdests
c.jumpdests = parent.jumpdests
} else {
c.jumpdests = make(destinations)
}

c.Gas = gas
c.value = value

return c
}

AsDelegate将合约设置为委托调用并返回当前合同(用于链式调用)

1
2
3
4
5
6
7
8
9
10
11
12
// AsDelegate sets the contract to be a delegate call and returns the current
// contract (for chaining calls)
func (c *Contract) AsDelegate() *Contract {
c.DelegateCall = true
// NOTE: caller must, at all times be a contract. It should never happen
// that caller is something other than a Contract.
parent := c.caller.(*Contract)
c.CallerAddress = parent.CallerAddress
c.value = parent.value

return c
}

GetOp 用来获取下一跳指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// GetOp returns the n'th element in the contract's byte array
func (c *Contract) GetOp(n uint64) OpCode {
return OpCode(c.GetByte(n))
}

// GetByte returns the n'th byte in the contract's byte array
func (c *Contract) GetByte(n uint64) byte {
if n < uint64(len(c.Code)) {
return c.Code[n]
}

return 0
}

// Caller returns the caller of the contract.
//
// Caller will recursively call caller when the contract is a delegate
// call, including that of caller's caller.
func (c *Contract) Caller() common.Address {
return c.CallerAddress
}

UseGas使用Gas。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// UseGas attempts the use gas and subtracts it and returns true on success
func (c *Contract) UseGas(gas uint64) (ok bool) {
if c.Gas < gas {
return false
}
c.Gas -= gas
return true
}

// Address returns the contracts address
func (c *Contract) Address() common.Address {
return c.self.Address()
}

// Value returns the contracts value (sent to it from it's caller)
func (c *Contract) Value() *big.Int {
return c.value
}

SetCode ,SetCallCode 设置代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
// SetCode sets the code to the contract
func (self *Contract) SetCode(hash common.Hash, code []byte) {
self.Code = code
self.CodeHash = hash
}

// SetCallCode sets the code of the contract and address of the backing data
// object
func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) {
self.Code = code
self.CodeHash = hash
self.CodeAddr = addr
}

EVM.go

结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// 上下文为EVM提供辅助信息。 一旦提供,不应该修改。
type Context struct {
// CanTransfer 函数返回账户是否有足够的ether用来转账
CanTransfer CanTransferFunc
// Transfer transfers ether from one account to the other
// Transfer 用来从一个账户给另一个账户转账
Transfer TransferFunc
// GetHash returns the hash corresponding to n
// GetHash用来返回入参n对应的hash值
GetHash GetHashFunc

// Message information
// 用来提供Origin的信息 sender的地址
Origin common.Address // Provides information for ORIGIN
// 用来提供GasPrice信息
GasPrice *big.Int // Provides information for GASPRICE

// Block information
Coinbase common.Address // Provides information for COINBASE
GasLimit *big.Int // 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
}

// EVM is the Ethereum Virtual Machine base object and provides
// the necessary tools to run a contract on the given state with
// the provided context. It should be noted that any error
// generated through any of the calls should be considered a
// revert-state-and-consume-all-gas operation, no checks on
// specific errors should ever be performed. The interpreter makes
// sure that any errors generated are to be considered faulty code.
// EVM是以太坊虚拟机基础对象,并提供必要的工具,以使用提供的上下文运行给定状态的合约。
// 应该指出的是,任何调用产生的任何错误都应该被认为是一种回滚修改状态和消耗所有GAS操作,
// 不应该执行对具体错误的检查。 解释器确保生成的任何错误都被认为是错误的代码。
// The EVM should never be reused and is not thread safe.
type EVM struct {
// Context provides auxiliary blockchain related information
Context
// StateDB gives access to the underlying state
StateDB StateDB
// Depth is the current call stack
// 当前的调用堆栈
depth int

// chainConfig contains information about the current chain
// 包含了当前的区块链的信息
chainConfig *params.ChainConfig
// chain rules contains the chain rules for the current epoch
chainRules params.Rules
// virtual machine configuration options used to initialise the
// evm.
vmConfig Config
// global (to this context) ethereum virtual machine
// used throughout the execution of the tx.
interpreter *Interpreter
// abort is used to abort the EVM calling operations
// NOTE: must be set atomically
abort int32
}

构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// NewEVM retutrns a new EVM . The returned EVM is not thread safe and should
// only ever be used *once*.
func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM {
evm := &EVM{
Context: ctx,
StateDB: statedb,
vmConfig: vmConfig,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(ctx.BlockNumber),
}

evm.interpreter = NewInterpreter(evm, vmConfig)
return evm
}

// Cancel cancels any running EVM operation. This may be called concurrently and
// it's safe to be called multiple times.
func (evm *EVM) Cancel() {
atomic.StoreInt32(&evm.abort, 1)
}

合约创建 Create 会创建一个新的合约。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// create creates a new contract using code as deployment code.
func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) {
// Depth check execution. Fail if we're trying to execute above the
// limit.

// 首先检测当前evm执行的深度 默认不应该超过1024
if evm.depth > int(params.CallCreateDepth) {
return nil, common.Address{}, gas, ErrDepth
}
// 这个函数我们不在追踪 其功能就是检测是否调用方的金额大约value
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, common.Address{}, gas, ErrInsufficientBalance
}
// 首先获取调用者的nonce 然后再更新调用者的nonce 这个如果熟悉以太坊交易流程的话应该知道nonce的作用。
nonce := evm.StateDB.GetNonce(caller.Address())
if nonce+1 < nonce {
return nil, common.Address{}, gas, ErrNonceUintOverflow
}
// 首先获取调用者的nonce 然后再更新调用者的nonce 这个如果熟悉以太坊交易流程的话应该知道nonce的作用。
evm.StateDB.SetNonce(caller.Address(), nonce+1)
// We add this to the access list _before_ taking a snapshot. Even if the creation fails,
// the access-list change should not be rolled back
if evm.chainRules.IsBerlin {
evm.StateDB.AddAddressToAccessList(address)
}
// Ensure there's no existing contract already at the designated address
contractHash := evm.StateDB.GetCodeHash(address)
if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
return nil, common.Address{}, 0, ErrContractAddressCollision
}
// Create a new account on the state
// 既然已经创建好了合约地址 那么就要为这个合约地址创建账户体系
snapshot := evm.StateDB.Snapshot()
evm.StateDB.CreateAccount(address)
if evm.chainRules.IsEIP158 {
evm.StateDB.SetNonce(address, 1)
}
evm.Context.Transfer(evm.StateDB, caller.Address(), 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, AccountRef(address), value, gas)
contract.SetCodeOptionalHash(&address, codeAndHash)

if evm.Config.Debug {
if evm.depth == 0 {
evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
} else {
evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
}
}

// // 将evm对象 合约对象传入run函数开始执行 此函数是核心 等一会分析到Call入口的时候最终也会调用此函数
ret, err = run(evm, contract, nil)
ret, err := evm.interpreter.Run(contract, nil, false)

// Check whether the max code size has been exceeded, assign err if the case.
if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
err = ErrMaxCodeSizeExceeded
}

// Reject code starting with 0xEF if EIP-3541 is enabled.
if err == nil && len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon {
err = ErrInvalidCode
}

// if the contract creation ran successfully and no errors were returned
// calculate the gas required to store the code. If the code could not
// be stored due to not enough gas set an error and let it be handled
// by the error checking condition below.
if err == nil {
createDataGas := uint64(len(ret)) * params.CreateDataGas
if contract.UseGas(createDataGas) {
evm.StateDB.SetCode(address, ret)
} else {
err = ErrCodeStoreOutOfGas
}
}

// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in homestead this also counts for code storage gas errors.
if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
contract.UseGas(contract.Gas)
}
}

if evm.Config.Debug {
if evm.depth == 0 {
evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, err)
} else {
evm.Config.Tracer.CaptureExit(ret, gas-contract.Gas, err)
}
}
return ret, address, contract.Gas, err
}

// Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)
}

Call方法, 无论我们转账或者是执行合约代码都会调用到这里, 同时合约里面的call指令也会执行到这里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// Call executes the contract associated with the addr with the given input as
// parameters. It also handles any necessary value transfer required and takes
// the necessary steps to create accounts and reverses the state in case of an
// execution error or failed value transfer.

// Call 执行与给定的input作为参数与addr相关联的合约。
// 它还处理所需的任何必要的转账操作,并采取必要的步骤来创建帐户
// 并在任意错误的情况下回滚所做的操作。

func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}

// Fail if we're trying to execute above the call depth limit
// 调用深度最多1024
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
// Fail if we're trying to transfer more than the available balance
// 查看我们的账户是否有足够的金钱。
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, gas, ErrInsufficientBalance
}

var (
to = AccountRef(addr)
snapshot = evm.StateDB.Snapshot()
)
if !evm.StateDB.Exist(addr) { // 查看指定地址是否存在
// 如果地址不存在,查看是否是 native go的合约, native go的合约在
// contracts.go 文件里面
precompiles := PrecompiledContractsHomestead
if evm.ChainConfig().IsByzantium(evm.BlockNumber) {
precompiles = PrecompiledContractsByzantium
}
if precompiles[addr] == nil && evm.ChainConfig().IsEIP158(evm.BlockNumber) && value.Sign() == 0 {
// 如果不是指定的合约地址, 并且value的值为0那么返回正常,而且这次调用没有消耗Gas
return nil, gas, nil
}
// 负责在本地状态创建addr
evm.StateDB.CreateAccount(addr)
}
// 执行转账
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)

// initialise a new contract and set the code that is to be used by the
// E 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, snapshot, contract, input)
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in homestead this also counts for code storage gas errors.
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
// 如果是由revert指令触发的错误,因为ICO一般设置了人数限制或者资金限制
// 在大家抢购的时候很可能会触发这些限制条件,导致被抽走不少钱。这个时候
// 又不能设置比较低的GasPrice和GasLimit。因为要速度快。
// 那么不会使用剩下的全部Gas,而是只会使用代码执行的Gas
// 不然会被抽走 GasLimit *GasPrice的钱,那可不少。
contract.UseGas(contract.Gas)
}
}
return ret, contract.Gas, err
}

剩下的三个函数 CallCode, DelegateCall, 和 StaticCall,这三个函数不能由外部调用,只能由Opcode触发。

CallCode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// CallCode differs from Call in the sense that it executes the given address'
// code with the caller as context.
// CallCode与Call不同的地方在于它使用caller的context来执行给定地址的代码。

func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}

// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
// Fail if we're trying to transfer more than the available balance
if !evm.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, gas, ErrInsufficientBalance
}

var (
snapshot = evm.StateDB.Snapshot()
to = AccountRef(caller.Address()) //这里是最不同的地方 to的地址被修改为caller的地址了 而且没有转账的行为
)
// initialise a new contract and set the code that is to be used by the
// E The contract is a scoped evmironment 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, snapshot, contract, input)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
contract.UseGas(contract.Gas)
}
}
return ret, contract.Gas, err
}

DelegateCall

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// DelegateCall differs from CallCode in the sense that it executes the given address'
// code with the caller as context and the caller is set to the caller of the caller.
// DelegateCall 和 CallCode不同的地方在于 caller被设置为 caller的caller
func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}

var (
snapshot = evm.StateDB.Snapshot()
to = AccountRef(caller.Address())
)

// Initialise a new contract and make initialise the delegate values
// 标识为AsDelete()
contract := NewContract(caller, to, nil, gas).AsDelegate()
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))

ret, err = run(evm, snapshot, contract, input)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
contract.UseGas(contract.Gas)
}
}
return ret, contract.Gas, err
}

// StaticCall executes the contract associated with the addr with the given input
// as parameters while disallowing any modifications to the state during the call.
// Opcodes that attempt to perform such modifications will result in exceptions
// instead of performing the modifications.
// StaticCall不允许执行任何修改状态的操作,

func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}
// Fail if we're trying to execute above the call depth limit
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
// Make sure the readonly is only set if we aren't in readonly yet
// this makes also sure that the readonly flag isn't removed for
// child calls.
if !evm.interpreter.readOnly {
evm.interpreter.readOnly = true
defer func() { evm.interpreter.readOnly = false }()
}

var (
to = AccountRef(addr)
snapshot = evm.StateDB.Snapshot()
)
// 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, new(big.Int), gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))

// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in Homestead this also counts for code storage gas errors.
ret, err = run(evm, snapshot, contract, input)
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != errExecutionReverted {
contract.UseGas(contract.Gas)
}
}
return ret, contract.Gas, err
}

notice

true

This is copyright.

...

...

00:00
00:00