以太坊虚拟机工作原理深入刨析(中)
字数 1069 2025-08-22 12:23:24
以太坊虚拟机(EVM)工作原理深入剖析
1. 核心数据结构
1.1 合约相关结构体
ContractRef 接口
type ContractRef interface {
Address() common.Address
}
AccountRef 结构体
type AccountRef common.Address
func (ar AccountRef) Address() common.Address {
return (common.Address)(ar)
}
Contract 结构体
type Contract struct {
CallerAddress common.Address
caller ContractRef
self ContractRef
jumpdests map[common.Hash]bitvec
analysis bitvec
Code []byte
CodeHash common.Hash
CodeAddr *common.Address
Input []byte
Gas uint64
value *big.Int
}
1.2 EVM 核心结构
EVM 结构体
type EVM struct {
Context BlockContext
TxContext TxContext
StateDB StateDB
Depth int
chainConfig *params.ChainConfig
chainRules params.Rules
vmConfig Config
interpreters []Interpreter
interpreter Interpreter
abort int32
callGasTemp uint64
}
BlockContext
type BlockContext struct {
CanTransfer CanTransferFunc
Transfer TransferFunc
GetHash GetHashFunc
Coinbase common.Address
GasLimit uint64
BlockNumber *big.Int
Time *big.Int
Difficulty *big.Int
}
TxContext
type TxContext struct {
Origin common.Address
GasPrice *big.Int
}
2. EVM 初始化
NewEVM 函数
func NewEVM(blockCtx BlockContext, txCtx TxContext, statedb StateDB,
chainConfig *params.ChainConfig, vmConfig Config) *EVM {
evm := &EVM{
Context: blockCtx,
TxContext: txCtx,
StateDB: statedb,
vmConfig: vmConfig,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(blockCtx.BlockNumber),
interpreters: make([]Interpreter, 0, 1),
}
// EWASM 网络暂不支持
if chainConfig.IsEWASM(blockCtx.BlockNumber) {
panic("No supported ewasm interpreter yet.")
}
evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig))
evm.interpreter = evm.interpreters[0]
return evm
}
3. 合约创建流程
Create 函数
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) ([]byte, common.Address, uint64, error) {
contractAddr := crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr)
}
create 函数核心逻辑
- 深度检查:防止递归调用超过限制
- 余额检查:验证调用者有足够余额
- Nonce更新:递增调用者Nonce
- 地址冲突检查:确保目标地址无合约
- 创建账户:
evm.StateDB.CreateAccount(address) - 转账操作:
evm.Context.Transfer() - 合约初始化:
NewContract() - 代码执行:
run(evm, contract, nil, false) - 代码存储:检查大小限制并存储代码
- 错误处理:必要时回滚状态
4. 合约调用机制
4.1 Call 函数
func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte,
gas uint64, value *big.Int) ([]byte, uint64, error) {
// 递归和深度检查
if evm.vmConfig.NoRecursion && evm.depth > 0 {
return nil, gas, nil
}
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
// 余额检查
if value.Sign() != 0 && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, gas, ErrInsufficientBalance
}
snapshot := evm.StateDB.Snapshot()
p, isPrecompile := evm.precompile(addr)
// 地址不存在处理
if !evm.StateDB.Exist(addr) {
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
return nil, gas, nil
}
evm.StateDB.CreateAccount(addr)
}
// 转账操作
evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)
// 预编译合约处理
if isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
} else {
code := evm.StateDB.GetCode(addr)
if len(code) == 0 {
ret, err = nil, nil
} else {
contract := NewContract(caller, AccountRef(addrCopy), value, gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
ret, err = run(evm, contract, input, false)
gas = contract.Gas
}
}
// 错误处理
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
gas = 0
}
}
return ret, gas, err
}
4.2 预编译合约执行
func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64) ([]byte, uint64, error) {
gasCost := p.RequiredGas(input)
if suppliedGas < gasCost {
return nil, 0, ErrOutOfGas
}
suppliedGas -= gasCost
output, err := p.Run(input)
return output, suppliedGas, err
}
5. 解释器执行流程
Interpreter.Run 核心逻辑
- 深度管理:增加调用深度
- 只读模式设置:防止状态修改
- 初始化执行环境:内存、栈、程序计数器
- 主循环:
- 获取操作码:
contract.GetOp(pc) - 验证栈状态:检查minStack/maxStack
- 计算Gas消耗:静态Gas + 动态Gas
- 内存调整:
mem.Resize(memorySize) - 执行操作:
operation.execute(&pc, in, callContext)
- 获取操作码:
- 结果处理:根据操作类型处理返回数据
- PC更新:非跳转操作时递增PC
6. 关键操作流程
6.1 转账操作
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
db.SubBalance(sender, amount)
db.AddBalance(recipient, amount)
}
6.2 状态回滚
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
gas = 0
}
}
6.3 内存管理
mem.Resize(memorySize)
7. 安全机制
- 调用深度限制:
params.CallCreateDepth - Gas检查:静态和动态Gas计算
- 栈验证:操作前验证栈大小
- 只读模式:防止状态修改
- 状态快照:错误时回滚
- 代码大小限制:
params.MaxCodeSize
8. 调试支持
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
defer func() {
evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
}()
}
9. 关键设计要点
- 不可重用性:EVM实例不应重用
- 非线程安全:EVM不是线程安全的
- Gas消耗模型:精确计算和验证
- 状态隔离:通过快照实现
- 解释器抽象:支持多种解释器实现
- 预编译合约:高性能原生实现
以上内容全面覆盖了以太坊虚拟机的核心工作原理,包括数据结构、初始化流程、合约创建与调用机制、解释器执行流程以及关键的安全和调试机制。