• 0

  • 5

  • 收藏

细品区块链虚拟机(I)·EVM设计理念及优化

双花的区块链

专注提供技术干货•sysu-cui_jiahui

2年前

作者:郑伟林    审核:崔嘉辉

《细品区块链虚拟机》系列共两篇,包括EVM设计理念及优化WebAssembly。文章的主要描述对象是区块链虚拟机的设计理念和设计目标。

提及智能合约,相信大家并不陌生,甚至已经可以熟练上手。而提及到智能合约的运行载体—区块链虚拟机,可能相对比较少的人能知道其在区块链中的角色定位。本文主要从发展历史、设计理念,存在问题以及相关优化等方面对区块链虚拟机展开分析。

什么是虚拟机?

虚拟机的定义,最官方的解释是:指通过软件模拟的具有完整硬件系统功能的、运行在一个完全隔离环境中的完整计算机系统。可能大家听了也似懂没懂。下面我们将再作稍微详细的解释。

虚拟机的出现最早是为了解决计算机的分时租赁问题。最开始大型计算机是很多用户共用的,你用10秒,我用10秒,你觉得这台计算机一直是你的,我觉得它一直是我的,但是事实上我是需要不停的等待这10秒。最早虚拟机就是用软件方法在同一硬件上实现不同的“虚拟系统”或者是“虚拟计算机”,让用户觉得这个硬件就是自己的。这个时候虚拟机的目的是通过分割计算机资源和完全隔离程序来使程序共享同样的硬件。

随着个人计算机的出现,虚拟机的作用相对较小,也没有必要去用。直到1997年人发者在实现同一个应用时,由于操作系统的差异性,需要分别实现不同的版本。Java虚拟机出现便是为了解决这样的问题。开发者只要用Java语言开发同一份应用代码,便可以依托Java虚拟机在不同的操作系统上运行。当时最经典的一句宣传口号“一处编译,到处运行”便可以很好地体现虚拟机发挥的作用。

本质上来讲,虚拟机就是一个统一、抽象的作用,虚拟机有自己的语言,然后你可以在虚拟机上写东西,但是虚拟机下面可以有不同的硬件、不同的操作系统、不同的软件。虚拟机为啥叫虚拟机,就是它把下面的东西抽象掉了,你不需要虚拟机以下的东西是什么。

为什么区块链需要虚拟机?

理解了虚拟机带来“统一、抽象”的作用后,本能的认知告诉我,虚拟机的性能肯定比不过你直接调用物理机,当中会有些损耗。与直接在物理机上运行应用代码相比,虚拟机采用的是模拟的底层硬件指令来运行应用代码,一次性能必然会下降。那么区块链为什么需要给自己增加一层虚拟机,它需要统一什么?

简单来说就是区块链网络需要达成共识,所以要求所有人的计算结果是一样的,但是传统的虚拟机不支持这样,传统的虚拟机可能会因为底层硬件的不同输出不同的结果,所以V神需要写一个。V神曾经在接受采访的时候说过:“以太坊之所以选择写一套新语言Solidity,就是为了能实现智能合约代码的一致性”。

区块链要求网络中所有的节点执行相同的代码必须要有同样的结果,这个要求很高。因为在传统的虚拟机上,比如Java虚拟机里面,是达不到这一点的。因为有一些机器是64位,有一些是32位,10的负十次方在不同的位数的机子上运行结果是不一样的。这样的结果如果出现在区块链上,各个节点并不容易达成共识。因此,区块链需要一个基于软件层面的虚拟机,来屏蔽不同硬件的差异。牛逼的Vitalik的就写了第一版的以太坊虚拟机EVM。

此外,区块链虚拟机还得防攻击。不是任意的代码都可以在区块链虚拟机中执行,否则会有人来写无限循环执行的代码。因此,虚拟机要对代码的执行有资源限制,例如Vitalik便在以太坊虚拟机内引入gas机制实现了这一点。

在提高安全性的同时,区块链虚拟机牺牲了性能。Java虚拟机自出现以来,做了大量的优化,例如通过即时编译来优化代码。而作为诞生到现在才几年且设计初衷侧重于安全的区块链虚拟机来将,性能非常低。目前的区块链虚拟机大多数无法进行复杂的计算操作。此外,区块链虚拟机的性能优化也很复杂。Java虚拟机里面可以容忍不同虚拟机运行结果有少量差别,但这个在区块链是不行的,区块链里所有的结果都必须一样,因此优化起来也困难不少。

区块链虚拟机发展史

比特币脚本引擎

从狭义上来讲,比特币上是没有虚拟机的,而只是一个处理交易脚本的的基于栈的处理引擎,只要负责执行与交易相关的解锁脚本(ScriptSig)和锁定脚本(ScriptPubKey)。比特币脚本引擎是非图灵完备的。因为比特币并不需要处理负责的逻辑,而只需要处理简单的转账功能(即上述的两种脚本),其执行流程固定且相对稳定,因此一个非图灵完备的脚本执行引擎便以足够。

以太坊虚拟机

与比特币不同,以太坊的目标不只是成为一个去中心的转账系统,而是一个能够支持多样化的复杂场景的去中心化系统,因此需要设计一个图灵完备的虚拟机来运行各种各样的代码。以太坊虚拟机(Ethereum Virtual Machine, 下文简称EVM)是运行智能合约的隔离沙盒,在运行期间对外完全隔离。智能合约编译后的字节码被部署到区块链中。合约运行时,字节码被加载到EVM中解析和运行,其本质上是将每个字节码转化为物理机的机器码并运行,且在不同的物理机上运行结果一致。EVM有以下几个特点:

I. EVM是一个基于栈的虚拟机(区别于基于寄存器的虚拟机),用于执行智能合约,并使用以太坊的账户模型来进行价值传输。

II. EVM是图灵完备的。EVM通过引入gas机制来限制任意一段代码的执行时间,从而解决了停机不可解的问题。

III. EVM是一个隔离的环境,EVM内部运行的代码不能与外部有任何联系。

这里我们简单介绍一下什么是基于栈的虚拟机。基于栈的虚拟机有几个重要的特性:实现简单可移植,这也是以太坊选择基于栈的虚拟机的主要原因。在基于栈的虚拟机中,有个重要的概念,操作数栈,数据存取为先进先出。所有的操作都直接与操作数栈直接交互,例如:取数据,寸数据,执行相应的操作等。这样有一个好处:可以无视具体的物理机器架构,特别是寄存器。例如,x86汇编”ADD EAX, EBX”,就需要指定这次运算需要从什么地方取操作数,执行完结果存放在何处。但是基于栈的虚拟机的指令就无需指定,例如加法操作就一个简单的”Add”就可以了,因为默认操作数存放在操作数栈上,直接从操作数栈上pop出两条数据直接执行加法运算,运算后的结果默认存放在栈顶。基于栈的虚拟机缺点也很明显,就是速度慢,因为无论什么操作都需要经过操作数栈。

作为区块链2.0时代的虚拟机代表,以太坊官方提供的EVM的设计原理说明中阐述了EVM的设计目标:

I. 简单安全:操作码尽量少,数据类型尽量少,虚拟机结构尽可能少。

II. 结果明确:任何代码的计算结果必须是完全确定的。计算步骤必须是精确的,以便可以测量每个步骤消耗的gas

III. 节省空间:EVM组件尽可能紧凑,代码小

IV. 优化友好:应该易于优化

V. 专门为区块链设计

如果读者浏览过这个设计文档(https://ethfans.org/posts/510),会发现EVM的设计看上去都非常合理,但是目前在实践中,EVM还是存在很多的额问题,它和目前主流的技术以及设计范例存在很多不相兼容的地方。下文我们将对对EVM的缺陷和不足展开分析。

EVM的不足及缘由

a) 机器码长度为256位

目前主流的处理器CPU主要有以下四种选择来实现快速的数学运算:1)8bit整数;2)16bit整数;3)32bit整数;4)64bit整数。虽然在一些情况下,32bit比16bit要快,以及在x86架构中8bit数学运算并不是完全支持(无原生的除法和乘法),但是基本上不管你采用以上的任意一种快速运算的方法,都可以保证数据运算在若各个时间周期内完成,计算是十分迅速,往往在纳秒级别的。因此,这些位长的整数是都可以被目前的主流处理器“原生地”支持,不需要额外的操作。然而,EVM处于所谓运算速度和效率方面考虑,采用了非主流的255位宽整数。下面,我们通过x86汇编码来分析来解释为啥256位宽整数实际上导致了计算效率较低。

下图展示了两个32bit整数和两个256bit整数在X86处理器上的编码;以及两个64位整数和两个256位在X64处理器上的编码。

由上图可知,不管是X86和X64机器上,采用256bit整数最后转化为CPU指令都比采用处理器原生支持的整数长度(32或者64)要复杂得多,因此其计算效率远远比不上原生支持的。那么为啥以太坊还需要如此设计呢,V神难道没有认识到这一点吗?答案是当时是否定的。

V神之所以采用256位主要是考虑到:只需要设计256位数字的操作码,便可以统一处理不同位宽数字,减少操作码的种类。不管一个数字是用32位还是64位还是256位才能表达的,统一用256位来表示。这样,对于这个数字的大多数操作,例如加法,乘法等,只需要设计一种基于256位宽的操作码,这样可以让整个EVM的操作码种类少很多,本着“简单即安全”的理念,这种设计可以减少EVM的被攻击的点,但是在性能上也牺牲了很多。这样的设计是否可行还有待商榷。另外,以太坊官方声称:256位整数的设计符合密码学的计算需求,例如SHA256等等。然而,你会发现如今在智能合约上有密码学计算的需求是少之又少,所以这点解释也稍显牵强。

b) 缺少标准库

在开发Solidity智能合约的时候我们会经常碰到这个问题,因为Solidity没有标准库。人们只能不断的从一些开源软件中复制粘贴代码。首先这些代码的安全性无法保证,再加上人们会为了更小的Gas消耗而不断修改代码,这就有可能对他们的合约引入更严重的安全性问题。目前,很多基于EVM修改的区块链虚拟机,例如Qtum(量子链)X86虚拟机,都对这方面有很大的改进,主要手段是通过预编译合约的形式来创建智能合约的标准库。

c) 难以调试和测试

这个问题不仅是EVM的设计缺陷,也与其实现方式有关。EVM中定义的异常信息有以下几种:

一般情况下,大部分开发者遇到的异常就是“Out-of-Gas”,这样的错误信息反馈并没办法给正在debug的开发者提供太大的帮助,同时,EVM没有提供调试日志,也无法调用外部代码(例如test helpers和mock数据)。此外,以太坊本身很难生成一条测试网络的私链,即使成功,私链的参数与行为也与公链不同。目前,有一些项目正在做相关工作使整个过程变得简单,较出名的有Truffle项目。但是大多数项目在调试智能合约都是采用基于栈的调试模式。即原本一个逻辑语句被拆分为多个字节码进行调试,调试的时候必须一个字节码一个字节码往下调试,这对于开发者来讲特别不友好。

d) 不可修改的代码

智能合约在设计的主要考虑的重要问题之一就是可升级性,因为EVM中代码是完全不可修改的。由于EVM采用哈佛计算机结构,也就不可能将代码在内存中加载并执行,代码和数据是被完全分离的。目前只能够通过部署新的合约来达到升级的目的,这可能需要复制原合约中的所有代码,并将老的合约重定向到新的合约地址。给合约打补丁或是部分升级合约代码在EVM中是完全不可能的。

e) 不支持浮点数

在不同架构的机器上,实现浮点数快速运算且保证运算结果严格一致的难度是比较大,这也许是EVM舍弃在浮点数这种数据类型的主要原因(为了不影响共识)。也有另外一种解释说“没有人会在加密货币中采用浮点数”,这是一种相对狭隘的想法。浮点数有很多的应用实例,比如风险建模,科学计算以及其他一些范围和近似值比准确值更加重要的情况。

Qtum-x86 虚拟机的优化尝试

目前,有很多的新的区块链虚拟机都是基于EVM开发而来的,并且针对上述为问题展开相关的优化工作,例如Qtum(量子链)的X86虚拟机。Qtum-x86 虚拟机将支持丰富的编程语言,操作系统与虚拟机解耦,旨在将智能合约开发推向主流。下面我们主要从设计理念以及设计目标简单分析Qtum-x86 虚拟机。

a) 支持多种编程语言

能支持多种编程语言是设计x86虚拟机的一个重要原因。目前几乎所有的编译器都支持x86架构指令集,所以实际的字节码和架构支持已经非常完备。Qtum-x86虚拟机的首批主流语言包括 C,C++ 和 Rust。选择 C 和 C++ 是因为总体来说相对简单,而选择 Rust 是因为相对轻便而且其设计理念特别重视安全,可以很好的防止 bug。不管是哪种语言,目前都有很好的技术基础可以帮助快速实现高级语言到x86指令集的转化,主要简单的修改对应的编译工具链,便可以快速实现更多语言的接入。Qtum-x86虚拟机的一个目标便是通过支持多种编程语言以及已有的生态支持来降低智能合约的开发难度。

b) 简化的状态存储(消除EVM中的状态限制)

x86 虚拟机实现了全新的合约状态存储方法,对于 EVM 遇到一个比较棘手的是存储,一切都是 256 位或 32 字节。如果有多个 flag(True or False),就得想办法封装或者用更复杂的操作。x86 采用的是一个新的数据库,用于存储 x86 智能合约状态数据,可以存储大量你想要的数据作为通用键值存储的地方,键或值没有数据大小限制,可以使用任何从 1 字节到更长字节的 key,并将其指向相同长度的变量值。Qtum-x86通过引入一个基于差异进行操作的新型数据库DeltaDB来实现这一点。更多关于Qtum-x86在DeltaDB的设计可以参考https://www.chainnews.com/articles/458810486322.htm。

c) x86 标准库和优化的Gas模型

对于以太坊虚拟机,最常见的吐槽是缺乏标准程序库。这不仅是开发者的烦恼,也占据了宝贵的区块链存储空间。提供标准程序库不仅可以使 Qtum 量子链的区块链变“瘦”。准库是编译器的内置支持库,和操作系统自带的库类似,功能是通用的,因此需要一个完善的管理机制,比如随着生态系统变得越来越高效,这些库函数对应的 gas 模型可能需要进行调整,从而能够反映其真实的资源成本。所以对标准库的管理也会引入 DGP 的理念。所谓DGP(分布式自治协议),指的是通过智能合约管理区块链的每一部分功能。即通过投票的方式管理者区块链系统的发展路径,而不是通过硬分叉。因此,在Qtum中,可以通过智能合约的形式定期管理标准库(例如调准标准库某个函数消耗的gas),以让标准库的设计更加合理。

目前的Qtum-x86还处于一个初级发展的阶段,除了上面所说的优化之外,Qtum-x86还具备很多新的特性,例如:支持区块链数据的动态分析,允许segwit交易来创建和执行合约(Qtum采用UXTO模型)等。这些新的特性更多的是为Qtum的生态发展服务,并不是属于区块链虚拟机本身的东西,因此,这里不做更多的分析。

后记

本文我们介绍了区块链虚拟机的相关概念以及发展历史,主要是针对以EVM为代表的区块链虚拟机的设计理念和目标展开分析。本系列的下一篇文章将以区块链3.0的公链代表EOS的虚拟机为主要分析对象,跟大家分享基于WebAssembly的区块链虚拟机的设计理念和设计目标。

5

相关文章推荐

未登录头像

暂无评论