EVM有一个基于栈的架构,在一个栈中保存了所有内存数值。EVM的数据处理单位被定义为256位的“字”(这主要是为了方便处理哈希运算和椭圆曲线运算操作

这里所说的内存数值是指那些EVM字节码运行所需要的输入、输出参数数据和智能合约程序运行中所需要的局部变量等数据,而不是指下文中所提到的“内存”数据;下文中的“内存”是一个与栈共同存在的、独立的临时存储空间。

以太坊EVM的架构和执行上下文

虚拟机引擎

目前市面上比较主流的是BTC脚本引擎和以太坊虚拟机(Ethereum Virtual Machine, EVM)。

  • BTC事务由一套脚本引擎(Script)处理,Script是一种类Forth的、基于栈式模型的、无状态的、非图灵完备的语言。
  • EVM基于Account模型将智能合约代码以对外完全隔离的方式内部运行,实现了图灵完备的智能合约体系。
    • EVM类似于Java虚拟机(JVM),编译后基于字节码运行,开发时则可以使用高级语言实现,编译器会自动转化为字节码。
    • EVM。以太坊虚拟机是以太坊中智能合约的运行环境,并且是一个沙盒,与外界隔离。
    • 智能合约代码在EVM内部运行时,是不能进行网络操作、文件I/O或执行其他进程的。智能合约之间也只能进行有限的调用,这样保证了合约运行的独立性,并尽可能提高了运行时的安全性。

图灵完备

图灵完备的计算环境——以太坊虚拟机(EVM)。这就意味着在EVM上可以做所有的能想得到计算,包括无限循环。EVM指令包括一个JUMP的跳转指令,可让程序跳回前面的程序代码,也可以像条件判断语句那样做条件跳转,当满足一定条件时将程序跳转到另一个地方执行。另外,一个合约可以调用其他合约,这提供了潜在的递归调用的功能。

以太坊虚拟机

EVM模块主要分为三大模块:编译合约模块、Ledger模块和EVM执行模块。

  • 编译合约模块
    • 主要是对底层Solc编译器进行一层封装,提供RPC接口给外部服务,对用Solidity编写的智能合约进行编译。编译后将会返回二进制码和相应的合约ABI, ABI可以理解为合约的手册
  • Ledger模块
    • Ledger模块主要是对区块链Account系统进行修改和更新,Account一共分为两种,分别是普通Account(EOA)和智能合约Account,调用方如果知道合约Account地址则可以调用该合约,Account的每一次修改都会被持久化到区块链中。
  • EVM执行模块
    • EVM执行模块作为核心模块,主要功能是对事务中的智能合约代码进行解析和执行,一般分为创建合约和调用合约两部分。
    • 支持普通的字节码执行和JIT模式的指令执行

指令集

EVM指令由很多标准机器码指令组成,包含:

  • 算术和位运算逻辑操作
  • 执行上下文查询
  • 栈、内存和存储访问
  • 处理流程操作
  • 日志、跳转和其他操作

EVM模块执行流程

EVM执行事务的流程如图所示

(1) EVM接收到Transaction信息,然后判断Transaction类型是部署合约还是执行合约。如果是部署合约,执行指令集,来存储合约地址和编译后的代码;如果是执行合约或是调用合约,则使用EVM来执行输入指令集。

(2) 执行上一条指令集之后,判断EVM是否停机,如果停机则判断是否正常停机,正常停机则更新合约状态到区块链,否则回滚合约状态。如果不停机则回到上一步(1)进行判断。

(3) 执行完的合约会返回一个执行结果,EVM会将结果存储在Receipt回执中,调用者可以通过Transaction的哈希来查询结果。

EVM代码的正式执行模型

每轮执行时,通过调出代码的第pc(程序计数器)个字节,每个指令如何影响元组都有定义。例如,ADD将两个元素出栈并将它们的和入栈,将Gas减1并将pc加1; stack将顶部的两个元素出栈,并将第2个元素插入由第1个元素定义的合约存储位置,同样减少最多200的Gas值,并将pc加1。虽然有许多方法通过即时编译去优化以太坊,但以太坊的基础性的实施可以用几百行代码实现。

EVM是基于栈的虚拟机

以太坊合约的代码是使用低级的基于堆栈的字节码的语言写成的,被称为“以太坊虚拟机代码”或者“EVM代码”。代码由一系列字节构成,每一个字节代表一种操作。一般而言,代码执行是无限循环,程序计数器每增加一(初始值为零)就执行一次操作,直到代码执行完毕或者遇到错误、STOP或者RETURN指令。

EVM不是基于寄存器的,而是基于栈的虚拟机。因此所有的计算都在一个称为栈的区域内执行。栈最大有1024个元素,每个元素有256位。对栈的访问只限于其顶端,允许复制最顶端的16个元素中的一个到栈顶,或者是交换栈顶元素和下面16个元素中的一个。所有其他操作都只能取最顶的一个或几个元素,并把结果压在栈顶。当然可以把栈里的元素放到存储或者主存中。但是无法只访问栈里指定深度的那个元素,在那之前必须把指定深度之上的所有元素都从栈中移除才行。

EVM的指令集被刻意保持在最小规模,以尽可能避免可能导致共识问题的错误。所有的指令都是针对256位这个基本的数据单位进行的操作,具备常用的算术、位、逻辑和比较操作,也可以进行条件和无条件跳转。

执行指令部分的Gas

虚拟机(EVM)执行指令部分的Gas计算是最为复杂的。虚拟机(EVM)事务执行期间的所有操作,包括数据库读写、消息发送以及虚拟机采取的每个计算步骤都要消耗一定量的燃料,并且在不同参数和缓存影响的情况下都会对应有不同的燃料标价。图2-15是取自以太坊官方黄皮书中的非指令部分的燃料标价示例图。

黄皮书链接:https://ethereum.github.io/yellowpaper/paper.pdf

高级语言

以太坊的高级语言最后会编译成在EVM中执行的EVM字节码(bytecode),部署在以太坊区块链上。以太坊提供3种编程语言:Solidity、Serpent和LLL。


往期精彩回顾:

区块链知识系列
密码学系列
零知识证明系列
共识系列
调研系列
BTC系列
以太坊系列
EOS系列
Filecoin系列
联盟链系列
Fabric系列
智能合约系列
Token系列