EVM学习笔记(一):初识EVM

什么是EVM?

EVM(Ethereum Virtual Machine,以太坊虚拟机)是以太坊区块链的核心计算引擎,负责执行智能合约并维护全网状态的一致性。它是去中心化应用(DApps)运行的底层环境,通过确定性、隔离性和可验证性,确保所有节点在相同输入下产生相同输出。

1. EVM的核心定义

  • 虚拟计算机
    EVM是一个图灵完备的虚拟机,模拟了一台抽象的计算机,拥有自己的栈、内存、存储和程序计数器(PC)。它不依赖物理硬件,而是由以太坊节点(如矿工/验证者)在软件层面实现。
  • 智能合约执行环境
    开发者用Solidity等语言编写智能合约,编译后生成EVM可识别的字节码 (如PUSH1 0x60ADD等操作码)。EVM按顺序执行这些指令,操作合约状态(如修改变量、转账ETH)。
  • 去中心化共识的基础
    所有全节点独立执行同一份字节码,确保状态变更(如账户余额、合约存储)全网一致,这是区块链"不可篡改"和"信任最小化"的关键。

2. EVM的架构与组件

EVM的执行模型基于栈(Stack)、内存(Memory)和存储(Storage),三者分工明确:

  • 栈(Stack)

    • 256位宽的LIFO(后进先出)结构,用于临时存储操作数和中间结果。
    • 操作码如PUSH(压栈)、POP(出栈)、ADD(栈顶两数相加)直接操作栈。
    • 栈深度限制为1024,防止无限递归攻击。
  • 内存(Memory)

    • 线性可扩展的字节数组,初始为空,按需扩展(每次扩展需支付Gas)。
    • 用于存储临时数据(如函数参数、中间计算结果),合约执行结束后内存会被清空。
    • 操作码如MLOAD(从内存加载数据到栈)、MSTORE(将栈数据存入内存)。
  • 存储(Storage)

    • 持久化键值对数据库,关联到具体合约地址,存储合约的长期状态(如变量值)。
    • 修改存储需支付高昂Gas(因数据需写入区块链),操作码如SLOAD(读取存储)、SSTORE(写入存储)。

3. EVM的执行流程

以一笔调用合约方法的交易为例,EVM的执行步骤如下:

  1. 交易验证

    • 节点验证交易签名、发送方余额和Gas限制。
    • 若交易合法,将其加入待处理交易池。
  2. 初始化状态

    • 复制当前区块链状态(如账户余额、合约存储)到临时环境,避免直接修改主状态。
  3. 执行字节码

    • 按顺序解析合约字节码,操作栈、内存和存储。
    • 示例:执行a = 1 + 2(假设a为合约变量):
      • PUSH1 0x01 → 栈顶压入1
      • PUSH1 0x02 → 栈顶压入2
      • ADD → 栈顶两数相加,结果3留在栈顶。
      • SSTORE → 将栈顶值3存入存储变量a的地址。
  4. 状态更新与Gas结算

    • 若执行成功,将临时状态变更写入新区块,并扣除发送方Gas。
    • 若执行失败(如revert或Gas不足),回滚所有变更,但已消耗的Gas不退还(部分场景除外)。
  5. 全网同步

    • 新区块通过共识机制(如PoS)被全网接受,所有节点更新本地状态,确保一致性。

4. EVM的关键特性

  • 确定性(Determinism)

    相同输入(交易数据+初始状态)必产生相同输出,避免因节点环境差异导致状态分歧。

    • 反例:若EVM依赖系统时间,不同节点可能因时间不同执行不同逻辑,破坏共识。
  • 隔离性(Isolation)

    合约执行在沙盒环境中运行,无法直接访问文件系统、网络等外部资源,防止恶意代码攻击。

    • 外部交互需通过预编译合约(如ORACLE)或链下服务(如IPFS)实现。
  • Gas机制

    每条操作码消耗固定Gas(如SSTORE消耗20,000 Gas),防止无限循环或恶意代码占用节点资源。

    • 发送方需预付Gas,若Gas不足,交易自动回滚。

5. EVM的局限性

  • 性能瓶颈

    EVM的栈式设计和单线程执行模式限制了吞吐量(以太坊TPS约15-30),导致高拥堵时Gas费飙升。

    • 解决方案:Layer 2扩容方案(如Optimism、Arbitrum)和EVM升级(如以太坊2.0的eWASM)。
  • 存储成本高

    修改存储需支付高额Gas(因数据需永久存储在区块链上),限制了复杂应用的发展。

    • 优化方案:使用链下存储(如Ceramic)或状态通道(如State Channels)。

6. EVM与Web3生态的关系

  • 智能合约标准
    EVM支持ERC-20(代币)、ERC-721(NFT)等标准,催生了DeFi、NFT、GameFi等去中心化应用。
  • 跨链兼容性
    其他区块链(如BSC、Avalanche、Polygon)通过兼容EVM(EVM-Equivalent)吸引以太坊开发者,降低迁移成本。
  • 开发者工具链
    Solidity、Hardhat、Truffle等工具围绕EVM构建,形成完整的开发生态。

总结

EVM是以太坊的"心脏",通过虚拟化计算环境、确定性执行和Gas机制,实现了智能合约的可靠运行和全网状态一致。尽管存在性能和成本限制,但其设计哲学(如去中心化、抗审查)深刻影响了Web3的发展方向。随着Layer 2和EVM升级的推进,EVM将继续作为去中心化应用的核心基础设施,推动区块链技术的普及与创新。


EVM在做什么?

在Web3中,EVM(以太坊虚拟机)是智能合约执行的核心引擎,其运行机制与区块链的底层逻辑紧密交织。以下从技术原理和核心概念出发,分点解析问题:

1. EVM在执行什么?

EVM执行的是编译后的智能合约字节码 。开发者用Solidity等语言编写合约,编译后生成EVM可识别的1字节操作码(Opcodes),如ADD(加法)、SSTORE(存储数据)等。EVM通过栈式模型逐条解析这些指令,操作内存、存储和状态变量,最终完成合约逻辑。例如:

  • 简单合约contract C { uint a; function setA(uint _a) { a = _a; } }
    编译后,a = _a会被转换为PUSH _aSSTORE等操作码,EVM按顺序执行这些指令,将_a的值写入合约存储。

2. 一笔交易从哪来?

交易来源可分为两类:

  • 外部账户(EOA)发起:用户通过钱包(如MetaMask)签名后广播交易,如转账ETH或调用合约方法。
  • 合约账户发起 :合约内部通过CALLDELEGATECALL指令触发其他合约的执行(如ERC-20代币的transferFrom)。

所有交易需包含:

  • 发送方地址from):交易发起者的钱包地址。
  • 接收方地址to):目标地址(可为合约地址)。
  • 数据字段calldata):合约方法签名及参数(如setA(20)的编码数据)。
  • Gas限制与价格:防止无限循环攻击,并激励矿工打包交易。

3. 谁在执行合约?

所有全节点(包括矿工/验证者)均会执行合约。当交易被打包进区块后:

  1. 节点验证交易签名和余额。
  2. 模拟执行EVM字节码,更新本地状态(如修改合约存储变量)。
  3. 若执行成功,将状态变更写入新区块;若失败(如revert),回滚状态。

关键点 :EVM的确定性(Determinism)确保所有节点执行相同指令序列后,状态变更完全一致。

4. 为什么每个节点结果一样?

EVM通过以下机制保证结果一致性:

  • 确定性执行
    EVM无随机数、时间戳等外部依赖,相同输入(交易数据+初始状态)必产生相同输出。例如,所有节点执行a = 1 + 2均会得到a = 3
  • 状态机复制(SMR)
    区块链本质是状态机,所有节点从同一初始状态(如创世区块)出发,按顺序处理交易,逐步更新状态树(如Merkle Patricia Trie)。共识机制(如PoS)确保唯一合法区块链,防止分叉导致状态分歧。
  • 验证与回滚
    节点会验证每笔交易的执行结果(如余额是否充足)。若发现无效状态变更(如余额不足却尝试转账),会拒绝该区块,维持全网状态一致。

5. 核心概念解析

  • Transaction(交易)

    区块链上的基本操作单元,包含发送方、接收方、数据、Gas等字段。交易类型包括:

    • 普通转账fromto均为EOA,data为空。
    • 合约调用to为合约地址,data包含方法签名及参数。
  • Calldata(调用数据)

    交易中携带的编码数据,用于指定合约方法及参数。例如:

    • 方法签名:setA(uint256)的Keccak-256哈希前4字节为60fe47b1
    • 参数编码:20(uint256类型)会被左填充为32字节(64个十六进制字符),如0000000000000000000000000000000000000000000000000000000000000014
    • 最终calldata60fe47b10000000000000000000000000000000000000000000000000000000000000014
  • msg.sender

    合约方法调用者的地址。在合约内部可通过msg.sender获取调用方信息,实现权限控制(如仅允许所有者调用敏感方法)。例如:

    solidity 复制代码
    solidityfunction withdraw() public {
        require(msg.sender == owner, "Unauthorized");
        // 执行提现逻辑
    }
  • State Change(状态变更)

    交易执行导致的区块链状态更新,包括:

    • 账户余额变化(如ETH转账)。
    • 合约存储变量修改(如a = 20)。
    • 合约自毁(SELFDESTRUCT指令)。
  • Revert(回滚)

    当交易执行失败(如条件不满足、Gas不足)时,EVM会回滚所有状态变更,并消耗已使用的Gas(部分场景下可退款)。例如:

    solidity 复制代码
    solidityfunction setA(uint _a) public {
        require(_a > 0, "Value must be positive");
        a = _a;
    }

    若调用setA(0),交易会因require失败而回滚。

总结

EVM通过确定性执行和状态机复制机制,确保所有节点对同一交易的处理结果一致。交易作为状态变更的载体,其来源、数据(calldata)和调用者(msg.sender)共同决定了合约的执行逻辑。理解这些核心概念,是深入掌握Web3开发的关键。


EVM的数据存在哪里?

EVM(以太坊虚拟机)的三大存储结构------Stack(栈)、Memory(内存)、Storage(存储),是智能合约执行的核心数据管理机制。它们分工明确,分别处理临时数据、中间计算结果和永久状态,共同确保合约逻辑的正确性和区块链的不可篡改性。以下是详细解析:

1. Stack(栈)

核心特性
  • 深度限制
    栈是256位宽的LIFO(后进先出)结构 ,最大深度为1024 。超过限制会触发Stack overflow错误,防止无限递归攻击。

  • 临时数据存储
    用于存储操作数和中间计算结果,如函数参数、算术运算的中间值。例如:

    solidity 复制代码
    function add(uint a, uint b) public pure returns (uint) {
        return a + b; // 编译后:PUSH a, PUSH b, ADD
    }

    执行时,ab被压入栈顶,ADD指令将栈顶两数相加,结果留在栈顶供后续使用。

  • 极高性能
    栈操作(如PUSHPOPADD)的Gas消耗极低(通常为3 Gas),是EVM中最快的存储结构。

典型场景
  • 算术运算:加减乘除、位运算等。
  • 逻辑判断 :比较操作(如GTEQ)的结果存储。
  • 控制流 :条件分支(JUMPI)的跳转条件。
限制与注意事项
  • 深度限制
    避免递归调用或复杂嵌套逻辑导致栈溢出。例如:

    solidity 复制代码
    function recursive(uint n) public pure {
        if (n > 0) recursive(n - 1); // 递归深度超过1024会失败
    }
  • 数据范围
    栈操作数必须为256位整数(uint256int256),超出范围需手动截断或扩展。

2. Memory(内存)

核心特性
  • 临时生命周期
    内存是线性可扩展的字节数组 ,仅在函数执行期间存在,执行结束后自动销毁,不占用区块链存储空间。

  • 按需扩展
    初始为空,访问未分配的内存位置时自动扩展,每次扩展需支付Gas(扩展成本随大小递增)。例如:

    solidity 复制代码
    function readMemory() public pure {
        bytes32 data; // 声明内存变量(实际未分配)
        assembly { mstore(0x00, 0x1234) } // 手动分配并写入内存
    }
  • 中等性能
    内存操作(如MLOADMSTORE)的Gas消耗较高(通常为3 Gas + 扩展成本),但低于存储操作。

典型场景
  • 动态数据操作
    处理可变长度数据(如字符串、数组),避免栈溢出。例如:

    solidity 复制代码
    function concatStrings(string memory a, string memory b) public pure returns (string memory) {
        bytes memory ab = new bytes(bytes(a).length + bytes(b).length);
        // 手动拼接字符串到内存
        return string(ab);
    }
  • 中间计算结果
    存储复杂运算的中间结果,减少栈使用。例如:

    solidity 复制代码
    function complexCalc(uint a, uint b) public pure returns (uint) {
        uint[] memory temp = new uint[](2);
        temp[0] = a * 2;
        temp[1] = b * 3;
        return temp[0] + temp[1];
    }
  • 合约交互
    调用其他合约时,传递参数或接收返回值(如CALL指令的输入/输出数据)。

限制与注意事项
  • 扩展成本
    频繁扩展内存会导致Gas消耗激增,应尽量预分配足够空间。例如:

    solidity 复制代码
    // 低效:循环中不断扩展内存
    for (uint i = 0; i < 100; i++) {
        memoryArray[i] = i; // 每次循环可能触发扩展
    }
    
    // 高效:预分配内存
    uint[] memory memoryArray = new uint[](100);
    for (uint i = 0; i < 100; i++) {
        memoryArray[i] = i;
    }
  • 数据清零
    内存不会自动清零,敏感数据需手动覆盖(如用0x00填充)。

3. Storage(存储)

核心特性
  • 永久上链
    存储是键值对数据库,与合约地址绑定,数据永久存储在区块链上,即使合约销毁后仍可追溯。
  • 高昂成本
    修改存储需支付最高Gas(SSTORE基础成本为20,000 Gas,若修改非零值为零值(清空)则退款部分Gas)。
  • 合约状态核心
    存储合约的长期变量(如代币余额、用户权限、配置参数),是区块链"不可篡改"特性的直接体现。
典型场景
  • 状态变量存储
    合约中声明的状态变量(如uintmappingstruct)默认存储在Storage。例如:

    solidity 复制代码
    contract Token {
        mapping(address => uint) public balances; // 存储用户余额
        function transfer(address to, uint amount) public {
            balances[msg.sender] -= amount; // 修改存储
            balances[to] += amount;
        }
    }
  • 持久化配置
    存储合约的全局参数(如费率、白名单),确保重启后状态不变。

  • 跨交易状态
    维护合约在多次交易间的状态(如拍卖合约的最高出价)。

限制与注意事项
  • 成本优化

    • 避免频繁修改存储,尽量批量更新(如用数组替代多次SSTORE)。
    • 使用unchecked块减少冗余检查(Solidity 0.8+)。
    • 考虑用事件(Event)记录非关键数据,降低存储压力。
  • 数据布局
    Storage按**槽(Slot)**组织,每个槽32字节。复杂类型(如struct、动态数组)的布局需遵循Solidity规则,避免碰撞。例如:

    solidity 复制代码
    contract StorageLayout {
        uint256 a; // Slot 0
        uint256[2] b; // Slot 1-2
        struct User { uint id; address addr; } // Slot 3(紧凑打包)
        mapping(address => User) users; // 映射键通过Keccak-256哈希计算槽位
    }
  • 初始化成本
    首次写入存储槽的成本高于后续修改(首次SSTORE为20,000 Gas,后续修改为5,000 Gas)。

三大存储结构的对比总结

特性 Stack(栈) Memory(内存) Storage(存储)
生命周期 函数执行期间 函数执行期间 永久上链
存储内容 临时操作数、中间结果 动态数据、中间计算结果 合约状态变量、持久化数据
扩展性 固定深度(1024) 按需扩展 固定槽位(32字节/槽)
Gas成本 极低(3 Gas/操作) 中等(3 Gas + 扩展成本) 极高(20,000 Gas/首次写入)
访问速度 最快(CPU寄存器级) 较快(内存访问) 最慢(区块链I/O)
典型操作码 PUSH, POP, ADD MLOAD, MSTORE, MSIZE SLOAD, SSTORE

实际应用建议

  1. 优先使用栈
    简单计算和逻辑判断应尽量在栈上完成,避免内存/存储开销。
  2. 谨慎使用内存
    处理动态数据时预分配内存,避免频繁扩展;敏感数据需手动清零。
  3. 最小化存储修改
    将高频更新数据(如计数器)放在内存中,仅在必要时同步到存储;使用mapping替代数组降低访问成本。

通过合理利用三大存储结构,可以显著优化合约的Gas效率和执行性能,同时确保状态管理的正确性。

相关推荐
Black_mario23 分钟前
从 Stove Protocol 看下一代股票代币化的范式转变
区块链
电报号dapp1195 小时前
交易所开发:在数字金融的竞技场中构建信任的圣殿
金融·web3·去中心化·区块链·智能合约
MicroTech20255 小时前
微算法科技(NASDAQ: MLGO)区块链混合算法:实现云存储的去中心化隐私保护
科技·去中心化·区块链
TechubNews14 小时前
2026 年观察名单:基于 a16z「重大构想」,详解稳定币、RWA 及 AI Agent 等 8 大流行趋势
大数据·人工智能·区块链
区块链小八歌1 天前
Horizen隐私主网在Caldera正式上线,将隐私保护引入“链上网络“生态
区块链
ee_trade1 天前
EE TRADE易投合约网格机器人创建全指南
人工智能·机器人·区块链
MicroTech20251 天前
微算法科技(NASDAQ :MLGO)开发基于区块链的分层架构,实现大数据存储方案性能与扩展性提升
科技·架构·区块链
lsrsyx1 天前
iCoin:以技术与风控为核心的数字资产交易平台
区块链
缘友一世1 天前
区块链原理与体系架构
架构·区块链