以太坊虚拟机工作原理深入刨析(中)
字数 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 函数核心逻辑

  1. 深度检查:防止递归调用超过限制
  2. 余额检查:验证调用者有足够余额
  3. Nonce更新:递增调用者Nonce
  4. 地址冲突检查:确保目标地址无合约
  5. 创建账户evm.StateDB.CreateAccount(address)
  6. 转账操作evm.Context.Transfer()
  7. 合约初始化NewContract()
  8. 代码执行run(evm, contract, nil, false)
  9. 代码存储:检查大小限制并存储代码
  10. 错误处理:必要时回滚状态

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 核心逻辑

  1. 深度管理:增加调用深度
  2. 只读模式设置:防止状态修改
  3. 初始化执行环境:内存、栈、程序计数器
  4. 主循环
    • 获取操作码:contract.GetOp(pc)
    • 验证栈状态:检查minStack/maxStack
    • 计算Gas消耗:静态Gas + 动态Gas
    • 内存调整:mem.Resize(memorySize)
    • 执行操作:operation.execute(&pc, in, callContext)
  5. 结果处理:根据操作类型处理返回数据
  6. 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. 安全机制

  1. 调用深度限制params.CallCreateDepth
  2. Gas检查:静态和动态Gas计算
  3. 栈验证:操作前验证栈大小
  4. 只读模式:防止状态修改
  5. 状态快照:错误时回滚
  6. 代码大小限制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. 关键设计要点

  1. 不可重用性:EVM实例不应重用
  2. 非线程安全:EVM不是线程安全的
  3. Gas消耗模型:精确计算和验证
  4. 状态隔离:通过快照实现
  5. 解释器抽象:支持多种解释器实现
  6. 预编译合约:高性能原生实现

以上内容全面覆盖了以太坊虚拟机的核心工作原理,包括数据结构、初始化流程、合约创建与调用机制、解释器执行流程以及关键的安全和调试机制。

以太坊虚拟机(EVM)工作原理深入剖析 1. 核心数据结构 1.1 合约相关结构体 ContractRef 接口 AccountRef 结构体 Contract 结构体 1.2 EVM 核心结构 EVM 结构体 BlockContext TxContext 2. EVM 初始化 NewEVM 函数 3. 合约创建流程 Create 函数 create 函数核心逻辑 深度检查 :防止递归调用超过限制 余额检查 :验证调用者有足够余额 Nonce更新 :递增调用者Nonce 地址冲突检查 :确保目标地址无合约 创建账户 : evm.StateDB.CreateAccount(address) 转账操作 : evm.Context.Transfer() 合约初始化 : NewContract() 代码执行 : run(evm, contract, nil, false) 代码存储 :检查大小限制并存储代码 错误处理 :必要时回滚状态 4. 合约调用机制 4.1 Call 函数 4.2 预编译合约执行 5. 解释器执行流程 Interpreter.Run 核心逻辑 深度管理 :增加调用深度 只读模式设置 :防止状态修改 初始化执行环境 :内存、栈、程序计数器 主循环 : 获取操作码: contract.GetOp(pc) 验证栈状态:检查minStack/maxStack 计算Gas消耗:静态Gas + 动态Gas 内存调整: mem.Resize(memorySize) 执行操作: operation.execute(&pc, in, callContext) 结果处理 :根据操作类型处理返回数据 PC更新 :非跳转操作时递增PC 6. 关键操作流程 6.1 转账操作 6.2 状态回滚 6.3 内存管理 7. 安全机制 调用深度限制 : params.CallCreateDepth Gas检查 :静态和动态Gas计算 栈验证 :操作前验证栈大小 只读模式 :防止状态修改 状态快照 :错误时回滚 代码大小限制 : params.MaxCodeSize 8. 调试支持 9. 关键设计要点 不可重用性 :EVM实例不应重用 非线程安全 :EVM不是线程安全的 Gas消耗模型 :精确计算和验证 状态隔离 :通过快照实现 解释器抽象 :支持多种解释器实现 预编译合约 :高性能原生实现 以上内容全面覆盖了以太坊虚拟机的核心工作原理,包括数据结构、初始化流程、合约创建与调用机制、解释器执行流程以及关键的安全和调试机制。