一、认识WASM
最近越来越多的项目开始转向VNT使用的WASM,像EOS、Ontology,包括最初引入虚拟机EVM运行智能合约环境的以太坊,最近也开始转向使用WASM。
除以太坊外,一些其他项目EOS(c++),Polkadot(rust), Cardano(Haskell,rust)已经或者计划开发支持wasm的虚拟机。目前 WebAssembly 在以太坊下一代虚拟机(EWASM)以及 EOS、Dfinity 项目中被使用。
1.什么是WASM?
WASM ,全称:WebAssembly ,WebAssembly 或者 wasm 是一个可移植、体积小、加载快并且兼容 Web 的全新格式。
是一种可以使用非 Java 编程语言编写代码并且能在浏览器上运行的技术方案,也是自 Web 诞生以来首个 Java 原生替代方案。
WebAssembly(WASM)是一种新的编码方式,可以在现代的网络浏览器中运行,它是一种低级的类汇编语言,具有紧凑的二进制格式,可以接近原生的性能运行,并为诸如 C/C++/Rust 等语言提供一个编译目标,以便它们可以在 Web 上运行。WebAssembly 的开发团队分别来自 Mozilla、Google、Microsoft、Apple 等公司,标准由 W3C 组织制定,目前 WebAssembly 在以太坊下一代虚拟机(EWASM)以及 EOS、Dfinity 项目中被使用。
2.WASM的特点
高效
WebAssembly 有一套完整的语义,实际上 wasm 是体积小且加载快的二进制格式, 其目标就是充分发挥硬件能力以达到原生执行效率
安全
WebAssembly 运行在一个沙箱化的执行环境中,甚至可以在现有的 JavaScript 虚拟机中实现。在web环境中,WebAssembly将会严格遵守同源策略以及浏览器安全策略。
开放
WebAssembly 设计了一个非常规整的文本格式用来、调试、测试、实验、优化、学习、教学或者编写程序。可以以这种文本格式在web页面上查看wasm模块的源码。
标准
WebAssembly 在 web 中被设计成无版本、特性可测试、向后兼容的。WebAssembly 可以被 JavaScript 调用,进入 JavaScript 上下文,也可以像 Web API 一样调用浏览器的功能。当然,WebAssembly 不仅可以运行在浏览器上,也可以运行在非web环境下。
3.WASM的优势
EVM虽然有着较高的兼容性,但需要预编译,同时需要付出gas作为代价,有着很高的编程成本,这种程序本质上都是脚本程序,即由程序翻译指令并执行,而不是由本地机器CPU读取指令并执行,因此效率非常低。
而Java的操作相对重复繁琐,在执行过程中耗时较长。
和EVM及Java 相比,WASM是谷歌、苹果、微软三大竞争公司同时支持的一种中间代码(字节码), 被所有流行浏览器支持。同时所有其他语言(c, c++, java)编写的程序都可以编译成WASM字节码,基于此建立的应用层生态不仅可以让开发人员有着非常低的学习成本, 而且有着高性能的标准。
由于EVM需要预编译,同时需要付出gas作为代价,实际上在EVM上编程成本很高。同时对于EVM的臃肿毫无帮助。最后,Solidity相比其语言基础C比较难学。
而Wasm是是内存安全的、平台独立的,并且可以有效地映射到所有类型的CPU架构。其指令集效率高,同时保有足够的可移植性。此外,Wasm指令集可以很容易地通过移除浮点指令来确定化,这将使它适合于替换EVM语言。
同时,Wasm在不增加内存消耗的情况下,可以达成无信任编程。可以通过在Wasm上进行堆栈分析与计量进行精确计算。
目前ETH打算将DApp也用上基于eth-WASM的智能合约,而EOS、Polkadot和Ontology则是用于虚拟机。其余涉及WASM的项目似乎并没有真正理解WASM是做什么的。
4.WASM有哪些对开发者友好的特点?
无疑,WASM 拓展了智能合约开发者可用的编程语言,这意味着你可以使用任何你熟悉的编程语言开发智能合约,并有着诸多对开发者友好的特点,例如:
1、 性能高效:WASM采用二进制编码,在程序执行过程中的性能优越;
2、 存储成本低:相对于文本格式,二进制编码的文本占用的存储空间更小;
3、 多语言支持:用户可以使用 C/C++/RUST/Go等多种语言编写智能合约并编译成WASM格式的字节码。
5.VNT Chain(维特链)基于WASM有哪些创新?
VNT Chain采用的是由VNT_P(公有链)+VNT_C(联盟链)+VNT_I(跨链)
的聚合链技术架构,其中VNT_P可运行被编译为WASM格式的智能合约,而无需关心智能合约的编写语言。正是因为看到WASM的诸多优势及未来前景,VNT技术支撑方云象在VNT Chain设计之初,,就确定了基于WASM开发VNT Chain虚拟机的技术方案。基于原生的WASM,VNT Chain进行了以下创新及改进:
1、 改造原生 WASM中多线程、浮点数、异常处理等导致计算非确定性的设计,确保计算的确定性;
2、扩展对string、address、hash等数据类型的支持;
3、扩展opcode以支持标准库的方法;
4、 增加燃料(Gas)机制,解决了智能合约中的停机问题;
5、 提供沙箱运行环境,实现资源隔离,保障系统的安全性;
6、 提供编写智能合约所需的各种语言的类库;
7、支持合约调试功能;
8、兼容自主设计智能合约编写语言-Q语言。
基于对WASM语言的应用以及优化改造,VNT Chain的智能合约模块具备更高的兼容性与性能,同时拥有更强的安全性与灵活性。尤其是VNT Chain首创的形式化自我验证能力编程语言,将语言本身的类型系统与数学定理系统结合,并将语言的类型验证与形式化验证结合,为智能合约形式化安全验证提供强有力的支持。
WASM从最初公布不被看好,到现在被广泛使用,我们可以肯定未来它将作为分布式应用开发的基础层被运用到越来越多项目中。
6.WebAssembly在大型项目中的应用
在这里能够举的例子还是很多,比如AutoCAD、GoogleEarth、Unity、Unreal、PSPDKit、WebPack等等。拿其中几个来简单说一下。
1、AutoCAD
这是一个用于画图的软件,在很长的一段时间是没有Web的版本的,原因有两个,其一,是Web的性能的确不能满足他们的需求。其二,在WebAssembly没有面世之前,AutoCAD是用C++实现的,要将其搬到Web上,就意味着要重写他们所有的代码,这代价十分的巨大。
而在WebAssembly面世之后,AutoCAD得以利用编译器,将其沉淀了30多年的代码直接编译成WebAssembly,同时性能基于之前的普通Web应用得到了很大的提升。正是这些原因,得以让AutoCAD将其应用从Desktop搬到Web中。
2、Google Earth
Google Earth也就是谷歌地球,因为需要展示很多3D的图像,对性能要求十分高,所以采取了一些Native的技术。最初的时候就连Google Chrome浏览器都不支持Web的版本,需要单独下载Google Earth的Destop应用。而在WebAssembly之后呢,谷歌地球推出了Web的版本。而据说下一个可以运行谷歌地球的浏览器是FireFox。
7.什么时候使用WebAssembly?
说了这么多,我到底什么时候该使用它呢?总结下来,大部分情况分两个点。
a.对性能有很高要求的App/Module/游戏
b.在Web中使用C/C++/Rust/Go的库
举个简单的例子。如果你要实现的Web版本的Ins或者Facebook, 你想要提高效率。那么就可以把其中对图片进行压缩、解压缩、处理的工具,用C++实现,然后再编译回WebAssembly。
8.WebAssembly的几个开发工具
AssemblyScript。支持直接将TypeScript编译成WebAssembly。这对于很多前端同学来说,入门的门槛还是很低的。
Emscripten。可以说是WebAssembly的灵魂工具不为过,上面说了很多编译,这个就是那个编译器。将其他的高级语言,编译成WebAssembly。
WABT。是个将WebAssembly在字节码和文本格式相互转换的一个工具,方便开发者去理解这个wasm到底是在做什么事。
9.WebAssembly的意义
WebAssembly并没有要替代JavaScript,一统天下的意思。我总结下来就两个点。
a.给了Web更好的性能
b.给了Web更多的可能
关于WebAssembly的性能问题,之前也花了很大的篇幅讲过了。而更多的可能,随着WebAssembly的技术越来越成熟,势必会有更多的应用,从Desktop被搬到Web上,这会使本来已经十分强大的Web更加丰富和强大。
10.EOS为何选择WASM
比特币的程序非常简单,由解锁脚本和锁定脚本构成。以太坊有智能合约,有图灵完备的虚拟机EVM,但是指令也相对简单,且自成一套。这两种程序本质上都是脚本程序,即由程序翻译指令并执行,而不是由本地机器CPU读取指令并执行,效率不高。但选择解释性语言有它的合理性,就是他的高度兼容性,它对智能合约的执行设备(矿机)没有限制。
那EOS的智能合约语言Web Assembly(wasm)有什么来头呢?它是谷歌、苹果、微软三大竞争公司同时支持的一种中间代码(字节码), 是浏览器都支持的一种代码。所有其他语言(c, c++, java)编写的程序都可以编程成wasm字节码的程序。看出这种设计的好处没?也就是说EOS兼容所有用c, c++等高级语言编写的程序,EOS的应用层生态基于此就建立了,开发人员的学习成本非常低。同时wasm字节码既可以编译成机器码后执行,又可以使用解释器直接执行, 兼容性和性能兼有,EOS选择了未来编程序语言,背靠Web Assembly生态, 至少在这方面它值得"区块链3.0”的称号。当然,wasm作为年轻的正在发展的技术,它的不稳定性可能会给EOS带来不好的影响,但EOS也还在开发中,且wasm本身具备柔性, 所以这个缺点并不重要了。
二、快速开始一个用C语言写的基于WASM的DEMO
1.前置条件
想要编译成WebAssembly,你首先需要先编译 LLVM。这是运行后续工具的先决条件。
Git。Linux 和 OS X 系统中好像已经默认装好了,在 Windows 上需要自行安装 Git。
CMake。在 Linux 和 OS X系统中,你可以使用包管理工具 apt-get 或 brew 来安装。如果是 Windows 系统,你可以自行下载安装。
系统编译工具。Linux上,安装 GCC。OS X 上,安装 Xcode。Windows 上安装 Visual Studio 2015 Community with Update 3 或更新版本。
Python 2.7.x,在 Linux 和 OS X上,很可能已经装好了。
安装完毕后,确认 git,cmake 和 python 已经在你的环境变量里,可以使用。
2,编译 Emscripten
通过 Emscripten SDK 构建 Emscripten 是自动的,下面是步骤。
$ git clone https://github.com/juj/emsdk.git
$ cd emsdk
$ ./emsdk install sdk-incoming-64bit binaryen-master-64bit
$ ./emsdk activate sdk-incoming-64bit binaryen-master-64bit
这些步骤完成以后,安装完成。将 Emscripten 的环境变量配置到当前的命令行窗口下。
$ source ./emsdk_env.sh
这条命令将相关的环境变量和目录入口将会配置在当前的命令行窗口中。
在 Windows中,./emsdk 使用 emsdk 代替,source ./emsdk_env.sh 使用 emsdk_env 代替。
3.编译并运行一个简单的程序
现在,我们已经有了一个完整的工具链,将简单的程序编译成 WebAssembly。不过,这里有一些值得提醒的地方:
在使用 emcc 命令时,要带着 -s WASM=1 参数(不然,默认将会编译成asm.js)。
如果我们想让 Emscripten 生成一个我们所写程序的HTML页面,并带有 wasm 和 JavaScript 文件,我们需要给输出的文件名加 .html 后缀名。
最后,当我们运行程序的时候,我们不能直接在浏览器中打开 HTML 文件,因为跨域请求是不支持 file 协议的。我们需要将我们的输出文件运行在HTTP协议上。
下面这些命令可能让你创建一个简单的“hello word”程序,并且编译它。
$ mkdir hello
$ cd hello
$ echo '#include <stdio.h>' > hello.c
$ echo 'int main(int argc, char ** argv) {' >> hello.c
$ echo 'printf("Hello, world!\n");' >> hello.c
$ echo '}' >> hello.c
$ emcc hello.c -s WASM=1 -o hello.html
我们可以使用 emrun 命令来创建一个 http 协议的 web server 来展示我们编译后的文件。
$ emrun --no_browser --port 8080 .
HTTP 服务开启后,您可以在浏览器中打开。如果你看到了“Hello,word!”输出到了 Emscripten 的 控制面板,恭喜你!你的 WebAssembly 程序编译成功了!
三、快速开始一个用golang写的基于WASM的DEMO
1.编写一个hello world的golang代码
package main
import (
"fmt"
)
func main() {
fmt.Println("hello,wasm,go")
}
2.编译
注:golang版本go1.11+,我电脑版本:go version go1.11.4 darwin/amd64
GOOS=js GOARCH=wasm go build -o test.wasm hello.go
3.把库中的html和js文件复制过来
cp $GOROOT/misc/wasm/wasm_exec.{html,js} .
我电脑用下面的命令
cp /usr/local/Cellar/go/1.11.4/libexec/misc/wasm/wasm_exec.html .
4.写一个http服务接口
package main
import (
"flag"
"log"
"net/http"
"strings"
)
var (
listen = flag.String("listen", ":8080", "listen address")
dir = flag.String("dir", ".", "directory to serve")
)
func main() {
flag.Parse()
log.Printf("listening on %q...", *listen)
log.Fatal(http.ListenAndServe(*listen, http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
if strings.HasSuffix(req.URL.Path, ".wasm") {
resp.Header().Set("content-type", "application/wasm")
}
http.FileServer(http.Dir(*dir)).ServeHTTP(resp, req)
})))
}
这里也可以不写http脚本,我电脑用的是caddy这个现成工具,是golang写的,nginx的升级版。
5.运行
go run http.go
在浏览器中打开http://localhost:8080/wasm_exec.html,点击 run 按钮,可以在控制台看到 hello,wasm,go字符串
这个字符串是wasm_exec.js(库中复制过来的)文件中的第45行打印出来的。
6.另一种更加直接的方式
node wasm_exec.js test.wasm
可以直接在控制台看到该字符串
四、EWASM项目初探
为了改进EVM1.0,以太坊的新一代虚拟机项目ewasm (github.com/ewasm)将支持WebAssembly(wasm),wasm在性能,扩展性,开发工具,社区都更有优势。除以太坊外,一些其他项目EOS,Polkadot, Cardano已经或者计划开发支持wasm的虚拟机。
以太坊基础设施的虚拟机(EVM)正准备进行一次彻底的重写。重写后,目前的 EVM 将被一个名为 EWASM 的新虚拟机所取代。EWASM 是以太坊的 WASM 版本(WebAssembly)代码,由万维网联盟(W3C)创建,它将允许开发者使用多种编程语言进行编码 ,而不仅仅是 Solidity语言 ,同时还增加了一些性能增强的功能。
EWASM 也存在技术问题。如,EWASM 支持多语言,因此代码很大程度上会依赖所谓的 “编译器” ,而这会成为攻击者的单点故障。
WebAssembly
1.什么是WebAssembly
wasm是可以在stack-based虚拟机上运行的一种二进制指令格式,设计的首要目标是性能,很适合用在区块链项目中。
WebAssembly二进制代码(wasm格式)可以转换成一种中间表示,一种可读的AST(抽象语法树,wast格式),这种可读的中间表示方便了编写和调试。
对扩展性的改进:wasm支持module import/export功能,可以将扩展部分做成module,在项目中导入。
2.WebAssembly的优势
可扩展性好
32/64位的操作符
支持更多的高级语言(C,Rust,Go等)
由W3C领导,Google,Apple,MicroSoft等大公司支持,更广阔的社区
主流浏览器支持,已有项目支持在非浏览器环境运行(wabt,wavm等)
3.EVM2.0对比EVM1.0的优势
性能更好
可扩展性好
开发合约更容易,支持更多语言和工具
4.ewasm 项目目标和方案
以太坊ewasm项目目标是既要完成对wasm的支持,又要兼容EVM1.0合约。要达到这个目标,ewasm目前的设计方案如下:
5.VM语义设计
执行合约前要能够判断出这个合约是wasm合约还是EVM1.0合约,并可调用对应的EVM解析执行。
如果客户端没有支持EVM1.0的EVM,需要将EVM1.0合约通过预编译合约(evm2wasm项目)翻译成wasm合约。
部署wasm合约时,要有一个预编译内置合约(合约名称Sentinel Contract)对合约进行检查,要求合约复合EVM2.0的要求,比如不能包括float类型,不能有限定外的操作。
6.ewasm合约语义设计(Ewasm Contract Interface - ECI)
ECI定义了ewasm合约的结构,通过wasm的module实现,实质上是wasm,可以认为ewasm是wasm的子集 。
合约必须符合wasm二进制格式
合约import module只能import EEI,不能import以太坊之外的module保证了EVM是一个沙盒程序。
提供debug module,但debug模式只能用于开发,在部署的时候需要去掉
每个合约必须提供两个export方法,一个是main,供VM执行调用,一个是memory,供EEI调用,保存结果。
wasm module中的start function需要被disable。
7.Ethereum Environment Interface (EEI) 定义
EEI定义了wasm程序如何与链进行交互,通过EEI将以太坊API暴露给ewasm,使ewasm可以读写链上信息,意义是wasm指令是low level的,high level功能留给客户端实现。
数据类型
bytes: 不确定长度的二进制数组
bytes32: 32字节的数组
address: 20字节的数组
u128: 128位的无符号整数
u256: 256位的无符号整数
API
useGas 减掉消耗的gas
getAddress 获得账户的地址
getBlockHash 获得区块哈希
call 将参数信息发送给某个地址
storageStore 保存一个256bit的值
更多API及详细信息参考https://github.com/ewasm/design/blob/master/eth_interface.md 。
8.以太坊内置合约
一些功能接口可以通过内置合约的方式来完成,每个合约都会有固定地址,可以像调用其他合约一样调用内置合约。
Sentinel合约:执行ewasm检查,看合约是否符合ECI的要求;执行 metering,将结果注入到ewasm合约中。
evm2wasm : 将EVM1.0合约翻译成wasm合约。
9.Metering
Metering功能是计算每个指令消耗的gas,在合约执行之前,提前知道这个合约需要多少消耗gas,也就可以预计运行时间。
提前计算gas消耗还可以避免因gas不足导致操作回滚,避免了做无用功。详细可参考 https://github.com/ewasm/design/blob/master/metering.md
10.ewasm****子项目
根据ewasm项目设计方案,ewasm创建了多个子项目分别实现模块功能,将这些项目组合到一起,共同实现支持wasm合约并且兼容旧合约的目标。
EVM-C
https://github.com/ethereum/evmc
EVM-C的作用是分离客户端和EVM,客户端在执行合约中只负责与链上信息的交互,把对合约的解析和执行任务交给EVM,他们之间的通信接口就是EVM-C。
客户端有多个版本的实现,Go,C++,Rust等。EVM也有多种方式去解析和执行合约,比如Interprter,JIT,AOT,现在又有了执行不同指令格式的合约的需求。所以通过接口将二者分离可以更方便的配置客户端。
EVMC由C++实现,可以直接与aleth结合。EVMC通过bind的方式支持非C++版本的客户端。目前只能支持aleth和geth。
Hera
Hera是由C++实现的尊从EVMC接口的ewasm虚拟机。Hera内部包含了wasm VM,目前wasm的VM有多个实现版本,Hera计划支持Bineryen,WABT,WAVM(EOS也使用WAVM),目前可以完全支持Bineryen。
不管是哪个版本的客户端,只要是使用EVMC接口就可以与Hera进行交互。总之只要执行指令过程中有与链上的信息交互就通过接口与客户端通信,其他low level 的 wasm指令由内置的VM执行。
对于EVM1.0的合约,Hera将通过另一个项目evm2wasm提供的方法将EVM1.0合约翻译成ewasm合约。
11.项目现状
Parity对ewasm合约的支持是最好的,但是它的实现方式是内置在客户端中,并没有使用EVMC和Hera,aleth和geth正在开发中。
目前的测试网络已经上线,但缺少工具支持,没有生成ABI文件的工具,也没有任何hello world教程或文档。
关于上线时间,根据DevCon4上的信息,以太坊会先发布Casper和Sharding,然后发布EVM2.0,至少要2020年左右甚至更久。