0%

StudyRecord-以太坊源码分析-EVM对象

Pre:

每处理一笔交易,就要新建一个 EVM对象 来执行交易中的数据。
这是在执行交易的入口 ApplyTransaction 函数中体现的:

core/state_processor.go
1
2
3
4
5
6
7
8
9
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) {

...

// 使用区块/交易相关信息,新建一个EVM对象,来处理交易
vmenv := vm.NewEVM(blockContext, vm.TxContext{}, statedb, config, cfg)

...
}

以太坊虚拟机EVM:

evm 模块的核心对象是 EVM,它代表了一个以太坊虚拟机,用于创建或调用某个合约。
EVM 对象内部主要依赖三个对象:

  • 解释器 Interpreter

  • 虚拟机相关配置对象 vm.Config

  • 以太坊状态数据库 StateDB

这3个对象后面会一一介绍,先看看EVM对象本身的源码。

EVM数据结构:

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
type EVM struct {
// Context provides auxiliary blockchain related information
// 辅助信息对象(包括GasLimit,BlockNumber等信息)
Context BlockContext

// 交易相关的信息(包括ORIGIN,GASPRICE)
TxContext

// StateDB gives access to the underlying state
// 为EVM提供StateDB相关操作
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.
// 虚拟机配置
Config Config

// global (to this context) ethereum virtual machine
// used throughout the execution of the tx.
// 解释器
interpreter *EVMInterpreter

// abort is used to abort the EVM calling operations
// NOTE: must be set atomically
// 用于中止EVM调用操作
abort int32

// callGasTemp holds the gas available for the current call. This is needed because the
// available gas is calculated in gasCall* according to the 63/64 rule and later applied in opCall*.
// 当前call可用的gas
callGasTemp uint64
}

创建EVM对象需要依赖以下信息:

  • BlockContext: 区块相关信息

  • TxContext: 交易相关信息

  • chainConfig:链配置信息

  • chainRules:链规则

  • Config:虚拟机解释器配置信息

  • interpreter:EVM 解释器

需要记录以下信息:

  • depth: 记录当前调用的栈的深度

  • abort: 用于中止EVM调用操作

  • callGasTemp: 当前call可用的gas

创建EVM所需信息:

BlockContext 区块信息:

其中BlockContext里包含了区块的相关信息,例如当前区块的矿工地址、当前区块号等

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
// BlockContext provides the EVM with auxiliary information. Once provided
// it shouldn't be modified.
// 上下文为EVM提供辅助信息。 一旦提供,不应该修改。
type BlockContext struct {
// CanTransfer returns whether the account contains
// sufficient ether to transfer the value
// 返回账户余额是否足够执行交易
CanTransfer CanTransferFunc

// Transfer transfers ether from one account to the other
// 将以太从一个帐户转移到另一个帐户
// 转账函数
Transfer TransferFunc

// GetHash returns the hash corresponding to n
// GetHash用来返回入参n对应的hash值
// 返回hash
GetHash GetHashFunc

// Block information
// 区块相关信息
Coinbase common.Address // Provides information for COINBASE // 挖出当前区块的矿工地址
GasLimit uint64 // Provides information for GASLIMIT // 当前区块 gas 限额
BlockNumber *big.Int // Provides information for NUMBER // 当前区块号
Time *big.Int // Provides information for TIME //
Difficulty *big.Int // Provides information for DIFFICULTY // 当前区块难度
BaseFee *big.Int // Provides information for BASEFEE // 当前区块的基础费用
Random *common.Hash // Provides information for RANDOM
}

TxContext 交易信息:

其中TxContext里包含了交易的相关信息:交易发起者、 交易的gas价格

1
2
3
4
5
6
7
8
9
// TxContext provides the EVM with information about a transaction.
// All fields can change between transactions.
// TXContext为EVM提供有关交易的信息。
// 所有字段都可以在交易之间发生变化。
type TxContext struct {
// Message information
Origin common.Address // Provides information for ORIGIN // 交易发起者
GasPrice *big.Int // Provides information for GASPRICE // 交易的 gas 价格
}

区块信息和交易信息在solidity文档的区块和交易属性有列举出来:

区块信息:

  • blockhash(uint blockNumber) returns (bytes32):指定区块的区块哈希 —— 仅可用于最新的 256 个区块且不包括当前区块,否则返回 0 。

  • block.basefee (uint): 当前区块的基础费用,参考: (EIP-3198 和 EIP-1559)

  • block.chainid (uint): 当前链 id

  • block.coinbase ( address ): 挖出当前区块的矿工地址

  • block.difficulty ( uint ): 当前区块难度

  • block.gaslimit ( uint ): 当前区块 gas 限额

  • block.number ( uint ): 当前区块号

  • block.timestamp ( uint): 自 unix epoch 起始当前区块以秒计的时间戳

交易信息:

  • tx.gasprice (uint): 交易的 gas 价格

  • tx.origin ( address ): 交易发起者(完全的调用链)

  • msg.data ( bytes ): 完整的 calldata

  • msg.sender ( address ): 消息发送者(当前调用)

  • msg.sig ( bytes4 ): calldata 的前 4 字节(也就是函数标识符)

  • msg.value ( uint ): 随消息发送的 wei 的数量

chainConfig 链配置信息

params/config.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
type ChainConfig struct {
ChainID *big.Int `json:"chainId"` // chainId identifies the current chain and is used for replay protection

HomesteadBlock *big.Int `json:"homesteadBlock,omitempty"` // Homestead switch block (nil = no fork, 0 = already homestead)

DAOForkBlock *big.Int `json:"daoForkBlock,omitempty"` // TheDAO hard-fork switch block (nil = no fork)
DAOForkSupport bool `json:"daoForkSupport,omitempty"` // Whether the nodes supports or opposes the DAO hard-fork

// EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150)
EIP150Block *big.Int `json:"eip150Block,omitempty"` // EIP150 HF block (nil = no fork)
EIP150Hash common.Hash `json:"eip150Hash,omitempty"` // EIP150 HF hash (needed for header only clients as only gas pricing changed)

EIP155Block *big.Int `json:"eip155Block,omitempty"` // EIP155 HF block
EIP158Block *big.Int `json:"eip158Block,omitempty"` // EIP158 HF block

ByzantiumBlock *big.Int `json:"byzantiumBlock,omitempty"` // Byzantium switch block (nil = no fork, 0 = already on byzantium)
ConstantinopleBlock *big.Int `json:"constantinopleBlock,omitempty"` // Constantinople switch block (nil = no fork, 0 = already activated)
PetersburgBlock *big.Int `json:"petersburgBlock,omitempty"` // Petersburg switch block (nil = same as Constantinople)
IstanbulBlock *big.Int `json:"istanbulBlock,omitempty"` // Istanbul switch block (nil = no fork, 0 = already on istanbul)
MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated)
BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin)
LondonBlock *big.Int `json:"londonBlock,omitempty"` // London switch block (nil = no fork, 0 = already on london)
ArrowGlacierBlock *big.Int `json:"arrowGlacierBlock,omitempty"` // Eip-4345 (bomb delay) switch block (nil = no fork, 0 = already activated)
GrayGlacierBlock *big.Int `json:"grayGlacierBlock,omitempty"` // Eip-5133 (bomb delay) switch block (nil = no fork, 0 = already activated)
MergeNetsplitBlock *big.Int `json:"mergeNetsplitBlock,omitempty"` // Virtual fork after The Merge to use as a network splitter

// TerminalTotalDifficulty is the amount of total difficulty reached by
// the network that triggers the consensus upgrade.
TerminalTotalDifficulty *big.Int `json:"terminalTotalDifficulty,omitempty"`

// Various consensus engines
Ethash *EthashConfig `json:"ethash,omitempty"`
Clique *CliqueConfig `json:"clique,omitempty"`
}

chainRules 链规则:

params/config.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Rules ensures c's ChainID is not nil.
func (c *ChainConfig) Rules(num *big.Int, isMerge bool) Rules {
chainID := c.ChainID
if chainID == nil {
chainID = new(big.Int)
}
return Rules{
ChainID: new(big.Int).Set(chainID),
IsHomestead: c.IsHomestead(num),
IsEIP150: c.IsEIP150(num),
IsEIP155: c.IsEIP155(num),
IsEIP158: c.IsEIP158(num),
IsByzantium: c.IsByzantium(num),
IsConstantinople: c.IsConstantinople(num),
IsPetersburg: c.IsPetersburg(num),
IsIstanbul: c.IsIstanbul(num),
IsBerlin: c.IsBerlin(num),
IsLondon: c.IsLondon(num),
IsMerge: isMerge,
}
}

返回chainConfig里属性的布尔值

Config 解释器配置信息:

core/vm/interpreter.go
1
2
3
4
5
6
7
8
9
10
11
12
13
// Config are the configuration options for the Interpreter
// 解释器配置类
type Config struct {

Debug bool // Enables debugging // 启用调试
Tracer EVMLogger // Opcode logger // 操作码记录器
NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages // 启用SHA3/keccak

JumpTable *JumpTable // EVM instruction table, automatically populated if unset // 操作码opcode对应的操作表

ExtraEips []int // Additional EIPS that are to be enabled
}

比较重要的属性是JumpTable操作码opcode对应的操作表

创建EVM函数:

core/vm/evm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM {
evm := &EVM{
Context: blockCtx,
TxContext: txCtx,
StateDB: statedb,
Config: config,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil),
}
// 创建EVM解释器
evm.interpreter = NewEVMInterpreter(evm, config)
return evm
}

NewEVM() 函数用来创建一个新的虚拟机对象,它有5个参数,含义分别如下:

  • blockCtx: 区块的信息和挖矿环境的函数和数据

  • txCtx: 交易的信息

  • statedb: 以太坊状态数据库对象

  • chainConfig: 当前节点的区块链配置信息

  • config: 虚拟机配置信息

NewEVM() 函数的实现,除了把参数记录下来以外,主要就是调用 NewEVMInterpreter() 创建一个解释器对象。

后面另外介绍 EVM的解释器对象

小结:

  • 以太坊在每处理一笔交易时,都会新建一个 EVM 对象来处理。

  • 创建一个EVM对象需要依赖以下信息:

    • BlockContext: 区块相关信息
    • TxContext: 交易相关信息
    • chainConfig:链配置信息
    • chainRules:链规则
    • Config:虚拟机解释器配置信息
    • interpreter:EVM 解释器
  • 需要记录以下信息:

    • depth: 记录当前调用的栈的深度
    • abort: 用于中止EVM调用操作
    • callGasTemp: 当前call可用的gas