技术解析 WebAssembly 智能合约特点与安全性
智能合约开发语⾔已经被 Solidity 统治了很⻓一段时间,⽤于开发可以在以太坊虚拟机 EVM 上运⾏的智能合约。不过,Solidity 有⼀些严重的问题,包括算术溢出、类型错误以及曾经冻结了 3 亿美元的 delegatecall 漏洞。所有这些漏洞都是在开发语⾔层⾯存在的问题。换句话说,如果有⼀个更优越的开发语⾔,本应该创造出更安全的智能合约。因此, WebAssembly (WASM)智能合约应运而生,知道创宇区块链安全实验室 从技术安全的角度,带你快速探析备受欢迎的 WebAssembly (WASM)智能合约 。
什么是 WebAssembly (WASM)智能合约?
WebAssembly (简称 WASM )是⼀种为栈式虚拟机设计的⼆进制指令集。WASM 被设计为可供类似 C/C++/Rust 等⾼级语⾔的平台编译⽬标。最初设计⽬的是解决 JavaScript 的性能问题。WASM 是由 W3C 牵头正在推进的 Web 标准,并得到了⾕歌、微软和 Mozilla 等浏览器⼚商的⽀持。
WASM 具有运⾏⾼效、内存安全、⽆未定义⾏为和平台独⽴等特点,经过了编译器和标准化团队多年耕耘,⽬前已经有了成熟的社区。在区块链领域,包括本体在内,当前已经有⼀些公链项⽬正准备⽀持使⽤ WASM 来运⾏智能合约。
WebAssembly 简要来说有以下三个特点 :
⼆进制格式,不同于 JavaScript 代码的⽂本格式
标准化,与 JavaScript ⼀样,实现了 WebAssembly 标准的引擎都可以运⾏ WebAssembly,不管是在服务器端还是浏览器端
快速,WebAssembly 可以充分发挥硬件的能⼒,以后你甚⾄可以在 WebAssembly 中使⽤ SIMD 或直接与 GPU 交互
当前的以太坊虚拟机按顺序处理交易。以太坊⽹络上的每个节点执⾏交易并将其存储在区块链上。为了允许通过分⽚进⾏ PoS 和并⾏交易处理 , 以太坊团队计划构建⼀个名为 eWASM 的新虚拟机。
根据 eWASM 的规范 :「要真正使以太坊作为世界计算机 , 我们需要有⼀个⾮常⾼性能的虚拟机。当前的虚拟机体系结构是原始性能的最⼤阻碍因素之⼀。WebAssembly 的⽬标是利⽤各种平台上可⽤的通⽤硬件功能 , 以接近本机速度执⾏。这将为需要性能 / 吞吐量的各种⽤途打开⼤⻔。」
值得注意的是,eWASM 不是⼀个智能合约开发语⾔,⽽是⼀个编译器的⽣成⽬标,它允许以太坊开发者使⽤其他语⾔(例如 Rust、C++ 等)开发智能合约并编译为以太坊接受的 WebAssembly。
eWASM 是 WebAssembly 的⼀个安全⼦集,它是 web 平台上相对新出现的编译⽬标。⽅便的是,WASM (以及 eWASM )模块可以在任何 JavaScript 项⽬中使⽤。对于⼤多数 dApp 应⽤代码来说,通常 75% 以上的代码根本都不是智能合约 —— ⽽是使⽤ JavaScript 与智能合约进⾏通信的代码。ewasm 和 JavaScript 使⽤同样的绑定和模块⽀持机制。
为什么选择 Rust 构建 WebAssembly?
随着 2017 年底,四⼤浏览器⼚商全部完成对 WebAssembly 的初步实现,以及 Webpack implementing fifirst-class support for WebAssembly 的消息公布,越来越多的团队在实现需求的时候将 WebAssembly 作为备选技术之⼀考虑。
Rust 作为语⾔是⼀种⾼效、可靠的通⽤⾼级语⾔。其⾼效不仅限于开发效率,它的执⾏效率也是令⼈称赞的,是⼀种少有的兼顾开发效率和执⾏效率的语⾔。Rust 速度惊⼈且内存利⽤率极⾼。由于没有运⾏时和垃圾回收,它能够胜任对性能要求特别⾼的服务,可以在嵌⼊式设备上运⾏,还能轻松和其他语⾔集成。
在探讨 WASM 在智能合约领域的巨⼤潜⼒时,前⾯提到 WASM 的⼀⼤优势就是⽀持有影响⼒的新锐编程语⾔,例如 Rust。使⽤ Rust 编写 WASM 具有如下优势:
可预⻅的性能
没有难以预料的 GC 暂停,也没有 JIT 编译器造成性能抖动,只有底层控制与上层⼈ 体⼯程学的完美结合。
更⼩的代码
代码尺⼨越⼩,⻚⾯加载速度就越快。Rust ⽣成的 wasm 模块不含类似于垃圾回收器这样的额外成本。⾼级优化和 Tree Shaking 优化可移除⽆⽤代码。
⽣态友好
充满活⼒的库⽣态系统助您旗开得胜。Rust 拥有丰富的表达能⼒和零成本的抽象,以及助⼒您学习的友好社区。
Rust 编译器⽬前⽀持两个 wasm 关联的⽬标 ( target ):
wasm32-unknown-unknown。此⽬标直接使⽤ llvm 后端编译成 wasm。它适合纯 rust 代码编译,譬如你没有 C 依赖的时候。跟 emscripten ⽬标⽐起来,它默认就⽣成更加洗练的代码, ⽽且也便于设置搭建。
wasm32-unknown-emscripten。此⽬标利⽤ emscripten ⼯具链编译成 wasm。当你具有 C 依赖的时候就得使⽤它了,包括 libc。
wasm32-unknown-unknown⼗分有望将新⽣的 Rust 代码融⼊ JS 项⽬中。
Rust & WebAssembly(WASM) 安全
Rust 被证明是可⽤于⼤型的、拥有不同层次系统编程知识的开发者团队间协作的⾼效⼯具。底层代码中容易出现种种隐晦的 BUG,在其他编程语⾔中,只能通过⼤量的测试和经验丰富的开发者细⼼的代码评审来捕获它们。在 Rust 中,编译器充当了守⻔员的⻆⾊,它拒绝编译存在这些难以捕获的 BUG 的代码,这其中包括并发 BUG。
使⽤ Rust 构建的区块链项⽬上,Libra 可以说知名度最⾼,在实现语⾔上,Libra (已更名为 Diem ) 项⽬选择了⼀个⾮常⼩众但宣称安全性突出的语⾔ RUST。然⽽,宣称的安全不表示实际上的安全。过于⼩众的语⾔往往缺乏⻓时间的锤炼,导致隐藏的问题较多。再安全的语⾔也⽆法确保实现的安全,漏洞常常来⾃代码实现过程,来⾃于⼈。
RUST 社区也在讨论随机数⽣成函数的安全升级。所以 RUST 语⾔虽然具有安全的特⾊,但是并不完美,不排除有可能成为 Libra 项⽬的阿喀琉斯之踵。最后,过于依赖 RUST 语⾔有可能带来单⼀性依赖问题,如果 RUST 语⾔出现安全问题,则会波及整个 Libra 系统,这⼀点可能需要时间来解决,毕竟 Libra 出现时间尚短,需要时间来实现必要的多样化部署。
虽然 Rust 在编译时会强制执⾏的内存安全保证。然⽽,Rust 还隐藏有第⼆种语⾔,它不会强制执⾏这类内存安全保证:这被称为不安全 Rust ( unsafe Rust )。它与常规 Rust 代码⽆异,但是会提供额外的超级⼒量。
⽐⽅说有⼀个 u8 ,它可以存放从零到 255 的值。那么当你将其修改为 256 时会发⽣什么呢?这被称为 「整型溢出」(「integer overflflow」 ),关于这⼀⾏为 Rust 有⼀些有趣的规则。当在 debug 模式编译时,Rust 检查这类问题并使程序 panic,这个术语被 Rust ⽤来表明程序因错误⽽退出。
在 release 构建中,Rust 不检测溢出,相反会进⾏⼀种被称为⼆进制补码包装(two’s complement wrapping)的操作。简⽽⾔之,256 变成 0,257 变成 1,依此类推。
近⼏年,Rust 语⾔以极快的增⻓速度获得了⼤量关注。其特点是在保证⾼安全性的同时,获得不输 C++/C++ 的性能,让系统编程领域难得的出现了充满希望的新选择。在 Rust 被很多项⽬使⽤以后,其实际安全性表现到底如何呢?
前⾯有⼀篇专⻔针对 Rust 安全的研究成果,针对近⼏年使⽤ Rust 语⾔的开源项⽬中的安全缺陷进⾏了全⾯的调查。这项研究调查了 5 个使⽤ Rust 语⾔开发的软件系统,5 个被⼴泛使⽤的 Rust 库,以及两个漏洞数据库。调查总共涉及了 850 处 unsafe 代码使⽤、70 个内存安全缺陷、100 个线程安全缺陷。
不安全 Rust 之所以存在,是因为静态分析本质上是保守的。当编译器尝试确定⼀段代码是否⽀持某个保证时,拒绝⼀些有效的程序⽐接受⽆效程序要好⼀些。这必然意味着有时代码可能是合法的,但是 Rust 不这么认为!在这种情况下,可以使⽤不安全代码告诉编译器,「相信我,我知道我在⼲什么。」这么做的缺点就是你只能靠⾃⼰了:如果不安全代码出错了,⽐如解引⽤空指针,可能会导致不安全的内存使⽤。
另⼀个 Rust 存在不安全⼀⾯的原因是:底层计算机硬件固有的不安全性。如果 Rust 不允许进⾏不安全操作,那么有些任务则根本完成不了。Rust 需要能够进⾏像直接与操作系统交互,甚⾄于编写你⾃⼰的操作系统这样的底层系统编程!
总结
不安全的 Rust 直接影响了以 Rust 构建的 WASM 智能合约的安全性,例如可能的整数溢出,导致转账⾦额前后出现巨⼤偏差等。好在编译器充当了守⻔员的⻆⾊,它拒绝编译存在这些难以捕获的 BUG 甚⾄是安全性的代码。
微信扫描关注公众号,及时掌握新动向
2.本文版权归属原作所有,仅代表作者本人观点,不代表比特范的观点或立场
2.本文版权归属原作所有,仅代表作者本人观点,不代表比特范的观点或立场