如何设计一个比 Ethereum 和 EOS 更好的虚拟机

2018-12-11 12:39:01 +08:00
 IOST

以太坊虚拟机( EVM )是一种“准图灵完整”的 256 位虚拟机,是以太坊网络最重要的组成部分之一。自以太坊以来,基于 EVM 的智能合约开发逐渐完善,并出现很多 DApp 应用,例如以太猫以及最近异常火爆的 Fomo3D 游戏等等。智能合约以及虚拟机的重要性已经得到了基本所有区块链开发者的认同,因此虚拟机的可用性、智能合约开发的便捷性已经成为各大公链竞争的主要赛道,同时也会决定一条公链最终能达到的高度。

一、以太坊虚拟机(EVM)并不图灵完备

网络上对以太坊虚拟机(EVM)有一种普遍的误解,就是认为以太坊虚拟机是图灵完备的,然而并非如此。

图灵机是阿伦图灵在 1936 提出的数学模型。图灵机包括一条无限长的纸带,一个字符表,一个读写头,一个状态寄存器,一个有限的指令集。运算开始时,图灵机读写头从某一位置开始按照此刻的配置(当前所处位置和当前格子内容等)来一步步的对照着指令集去进行操作,直到状态变为停止,运算结束。在计算领域,我们研究的一切问题都是计算问题,图灵完备意味着任意可计算问题都可以被解决。编程语言或虚拟机的本质都是一个图灵机,某种编程语言或者虚拟机是图灵完备意味着它可以做到图灵机能做到的所有事情,即可以解决所有的可计算问题。

而在以太坊虚拟机的设计中,因为指令的计算受到 gas 的约束,所以这就限制了可完成的计算次数。这也是一种图灵不完备的常见原因,因为循环、递归或计算的有界导致程序保证终止,所以 EVM 上能运行的程序会受到很多限制,EVM 并不是图灵完备的。

二、EVM 的不合理设计

以太坊的 EVM 作为最早的准图灵完备的虚拟机,在开创了面向智能合约的 DApp 开发的同时,随着区块链应用越来越广泛,EVM 最初的一些不合理设计逐渐显现,甚至有些设计会导致严重的安全问题。单就以太坊虚拟机层面,我们认为在设计层面和安全层面有以下这些问题:

01 智能合约设计层面

缺乏标准库支持:EVM 缺少完善的标准库支持,甚至最基本的 string 类型支持,在 EVM 中都很鸡肋,例如字符串拼接、切割、查找等等都需要开发者自己实现。带来的后果就是开发者需要关注更多非本身业务的零碎细节,不能专注本身业务开发。同时自行实现的类库可能会因为时间、空间复杂度太高,消耗大量无谓的 gas,又或者开发者从开源项目中借鉴相关类库代码,但也会引入更多安全性方面的问题,加重合约代码审计的复杂度,亦是得不偿失。 难以调试和测试:EVM 难以调试和测试,EVM 除了能抛出 OutOfGas 异常之外,不会给开发者返回任何信息,无法打印日志、要做到断点、单步调试更是完全不可能。虽然 event 机制可以部分改善这个问题,但 event 机制的本身设计就决定了他不是一个优雅好用的调试工具。 不支持浮点数:EVM 不支持浮点数,以太坊以 Wei 为最小单位,只有整数,不支持其他粒度的计量,这种设计避免了引入浮点数导致的精度问题,但开发者在实际开发中,为了表示一个 eth 变量,就会在变量后面跟很多 0,导致代码维护极度复杂。同时不可否认,浮点数在特定的场景下,还是有很大的利用价值的,不能一刀切直接放弃引入。 合约不能升级:EVM 不支持合约升级,合约升级是智能合约开发中的一个强需求,也是每一个合约开发者必须要考虑的问题,合约升级可以实现给现有合约打安全补丁、扩展现有合约功能等等。EVM 完全不支持升级,开发者只能通过发布新合约来解决这个问题,费时费力。

02 智能合约安全层面

溢出攻击,EVM 的 safeMath 库不是默认使用,例如开发者对 solidity 的 uint256 做计算的时候,如果最终结果大于 uint256 的最大值,就会产生溢出变为一个很小的数,这样就产生了溢出漏洞。诸如 BEC、SMT 等相关币种都遭受过溢出攻击,带来了极度严重都后果,BEC 溢出漏洞如下:

重入攻击,solidity 一大特性是可以调用外部其他合约,但在将 eth 发送给外部地址或者调用外部合约的时候, 需要合约提交外部调用。如果外部地址是恶意合约,攻击者可以在 Fallback 函数中加入恶意代码,当发生转账的时候,就会调用 Fallback 函数执行恶意代码,恶意代码会执行调用合约的有漏洞函数,导致转账重新提交。最严重的重入攻击发生在以太坊早期,即知名的 DAO 漏洞。下面合约片段具体阐述了重入攻击:

非预期函数执行,EVM 没有严格检查函数调用,如果合约地址作为传入参数可控,可能导致非预期行为发生。具体如下:

综上,EVM 在设计和安全层面都存在不少问题,虽然 EVM 团队又开发出了 Vyper 这种新的合约开发语言,但一直处于实验阶段,不能实际生产使用。在以太坊被大规模应用到 DApp 开发的今天,各种问题不断累积将最终导致其积重难返。

三、现象级公链 EOS 的明显问题

EOS 是继以太坊之后,又一现象级的公链应用,有自己独立的一套基于 WebAssembly 的智能合约引擎,但目前 EOS 合约开发有如下几个明显问题:

账户系统不友好:创建账户操作难度大,创建账户后才能发布合约,EOS 需要使用已有账户去创建新账户,寻找一个拥有 EOS 账户的朋友或第三方,对任何人来说都不是一件很容易的事。创建账号需要购买 RAM,就是需要花钱建账号,如果找第三方帮忙创建账号存在资金风险。创建账号后,还需要抵押 EOS 换取 CPU 使用时间和 net 带宽,才能在 EOS 网络做操作。这些操作对于开发者来说过于繁琐。 RAM 价格:RAM 价格昂贵,合约运行必须用到 RAM,EOS 开通了 RAM 市场,用于交易内存,虽然说 RAM 可以买卖,但依然存在很多人炒作,导致 RAM 价格昂贵。 开发难度大:使用 C++作为合约开发语言,极大的提高了合约开发门槛,C++本身就极为复杂,在其上还要去调用 EOS.IO C++ API 完成智能合约开发,对开发者的个人能力要求极高。

因此面对种种可列举的问题,EOS 智能合约开发对于开发者吸引力不大,甚至会成为开发者放弃 EOS 的理由。

四、IOST 虚拟机的诞生

我们认为一个良好的虚拟机实现必须在做到架构设计优雅的同时满足易用性和安全性的需求,在经过对比参考 EVM、EOS、C Lua、V8 等相关虚拟机的优缺点之后,我们从根源上解决了很多 EVM 和 EOS 不合理性设计与问题,并且基于 V8 在 NodeJs 和 Chrome 上的优异表现,最终构建了基于 V8 的 IOST 虚拟机。

01 IOST V8VM 架构与设计

VMManager 系统构架

V8VM 架构的核心是 VMManger,主要有如下三个功能:

VM 入口,对外接收其他模块的请求,包括 RPC 请求、Block 验证、Tx 验证等等,预处理、格式化后交给 VMWorker 执行。 管理 VMWorker 生命周期,根据当前系统负载灵活设置 worker 数量,实现 worker 复用;同时在 worker 内部实现了 JavaScript 代码热启动、热点 Sandbox 快照持久化功能,减少了频繁创建虚拟机、频繁载入相同代码引发的高负载、内存飙升问题,降低系统消耗的同时,又极大的提高了系统吞吐量,使得 IOST V8VM 在处理 fomo3D 这种典型的海量用户合约时游刃有余。 管理与 State 数据库的交互,保证每一笔 IOST 交易的原子性,在合约执行出错,或者 gas 不足的情况下,能够回退整个交易。同时在 State 数据库中,也是实现了两级内存缓存,最终才会 flush 到 RocksDB 中。

02 Sandbox 核心设计

IOST Sandbox 系统构架图

Sandbox 作为最终执行 JavaScript 智能合约的载体,对上承接 V8VM,对下封装 Chrome V8 完成调用,主要分为 Compile 阶段和 Execute 阶段:

Compile 阶段

主要面向合约开发和上链,有如下两个主要功能:

Contract Pack,打包智能合约,基于 webpack 实现,会打包当前合约项目下的所有 JavaScript 代码,并自动完成依赖安装,使 IOST V8VM 开发大型合约项目变成可能。同时 IOST V8VM 和 Node.js 的模块系统完全兼容,可以无缝使用 require、module.exports 和 exports 等方法,赋予合约开发者原生 JavaScript 开发体验。 Contract Snapshot, 借助 v8 的 snapshot 快照技术,完成对 JavaScript 代码的编译,编译后的代码提升了 Chrome V8 创建 isolate 和 contexts 的效率,真正执行时只需要反序列化快照就可以完成执行, 极大的提高了 JavaScript 的载入速度和执行速度。

Execute 阶段

主要面向链上合约真正执行,有如下两个主要功能:

LoadVM,完成 VM 初始化,包括生成 Chrome V8 对象、 设置系统执行参数、导入相关 JavaScript 类库等等,完成智能合约执行之前的所有准备工作。部分 JavaScript 类库如下:

Execute,最终执行 JavaScript 智能合约,IOST V8VM 会开辟单独的线程执行合约,并监控当前执行状态,当发生异常、使用资源超过限制、执行时间超过最大限制时,会调用 Terminate 结束当前合约执行,返回异常结果。

03 IOST V8VM 性能表现

我们认为作为公链最核心的底层设施,虚拟机必须在性能上表现足够优异。IOST 在设计、虚拟机选型之初,就把性能作为最重要的指标之一。

Chrome V8 使用 JIT、内联缓存、惰性加载等方式实现 JavaScript 的解释执行,得益于 Chrome V8 的高性能,IOST V8VM 的 JavaScript 执行速度有了质的提升。我们在递归 fibonacci、内存拷贝、复杂 cpu 运算这三个方面,分别测试了 EVM、EOS、C Lua 和 V8VM 的性能,具体结果如下:

测试系统环境

测试结果

实测 IOST V8VM 在主流 VM 实现中,性能表现优异。上述测试包含了虚拟机启动和加载配置的时间,可见 IOST V8VM 直接冷启动也有不少的性能优势,后续我们还会加入 VM 对象池、LRU 缓存等等,来提升虚拟机 CPU、内存使用率,以更好提升 IOST 处理智能合约的能力。

04 结语

目前我们已经完成了 IOST V8VM 虚拟机第一版的开发,在第一版中,我们已经实现了所有预定的功能,在后续的迭代过程中,我们将把 IOST V8VM 的安全性、易用性放在首位,并在如下三个方向不断努力:

高性能,保证合约更快的执行 开发上手更加简单,增加并完善更多标准库 支持大型项目构建,调试,有完善的工具链

同时在第一版 IOST V8VM 的开发中我们初步验证了很多想法,例如投票、合约域名、token 等等,更多的新功能、更多的新特性都会在接下来的测试网更新中逐步实现。

附录: 虚拟机 benchmark 程序

01 evm code

02 Lua Code

03 EOS Code

04 V8 code

2023 次点击
所在节点    以太坊
3 条回复
whileFalse
2018-12-11 13:12:59 +08:00
磁盘不够大是不是也是不完备的图灵机。
IOST
2018-12-25 13:09:33 +08:00
@whileFalse 是的这样的。目前做的是准图灵完备。
IOST
2018-12-25 13:10:16 +08:00
@whileFalse 不好意思回复的晚了 T T

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/516452

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX