在前两篇笔记中,我们梳理了以太坊的状态世界模型和账户系统,了解了以太坊如何像一个分布式的全球计算机,维护着一个不断变化的共享状态数据库,这个“全球计算机”究竟是如何执行指令、改变状态的呢?答案就在于以太坊的执行引擎——以太坊虚拟机,本篇笔记,我们将深入黄皮书(Yellow Paper)的第9章,详细解析EVM的执行模型,这是理解以太坊智能合约运行机制的核心。
EVM执行模型:一个基于栈的抽象计算机
从本质上讲,EVM是一个基于栈的、图灵完备的虚拟机,这个描述包含了几个关键概念:
- 图灵完备:意味着EVM可以执行任何可计算的算法,只要给予足够的时间和资源,这也带来了“停机问题”——我们无法预先判断一个智能合约是否会无限循环,因此需要引入Gas机制来防止合约消耗无限的计算资源。
- 基于栈:与大家更熟悉的基于寄存器(如x86架构)或基于累加器(如MIPS架构)的处理器不同,EVM的所有计算操作都发生在一个栈上,栈是一种后进先出的数据结构,就像一摞盘子,最后放上去的盘子最先被取走,EVM的栈深度被限制为1024层,以防止栈溢出攻击。
EVM的执行模型,就是一条指令接一条指令地执行,每一条指令都会根据当前的状态和栈的内容,改变栈的状态、内存的状态,并最终可能改变以太坊的全球状态。
执行的“燃料”:Gas机制
在深入指令集之前,我们必须理解贯穿整个EVM执行的灵魂——Gas。
Gas是以太坊网络中衡量计算、存储和带宽资源消耗的单位,每一笔交易发送时,发送者都需要设定一个Gas Limit( gas限制)和愿意支付的Gas Price( gas价格),Gas Limit是交易愿意消耗的最大Gas数量,而Gas Price是每个Gas单位的价格。
执行过程如下:
- Gas预支付:交易执行前,发送者账户会被扣除
Gas Limit * Gas Price的以太币,作为“预付款”。 - Gas消耗:EVM在执行每一条指令时,都会根据操作的复杂度消耗一定量的Gas,简单的加法消耗很少Gas,而写入存储或进行大量循环则会消耗大量Gas。
- Gas退款:某些操作,比如从合约中删除一个存储变量,会返还一部分Gas,这鼓励开发者清理不再需要的数据。
- 结算:
- 交易成功:所有剩余的Gas(
Gas Limit - 已消耗Gas)会按原价退还给发送者,成功执行的操作费(已消耗Gas * Gas Price)会作为矿工费(手续费)支付给打包该交易的矿工。 - 交易失败:如果在执行过程中Gas耗尽(Out of Gas),所有状态回滚到交易执行前的快照,但已消耗的Gas不会退还,这是对发送者发起无效计算的一种惩罚。
- 交易成功:所有剩余的Gas(
Gas机制确保了网络的安全性,防止了恶意合约(如无限循环)对网络造成拒绝服务攻击。
执行环境:一个结构化的上下文
EVM的执行并非在真空中进行,而是在一个精心设计的执行环境中,黄皮书用结构体 ExecutionEnvironment 定义了这个上下文,它包含了执行当前代码所需的所有信息,主要包括:
caller:调用者的地址,即发起交易的账户地址。value:随交易发送的以太币数量(以Wei为单位)。address:当前正在执行的合约的地址。code:当前正在执行的合约的字节码。data:伴随交易或调用传入的数据(Calldata)。gasprice