EVM和WASM(EOS虚拟机)基本原理
EVM是以太坊图灵完备的虚拟机(Ethereum Virtual Machine), 简称EVM
- 由程序翻译指令并执行
- EVM出于所谓运算速度和效率方面考虑,采用了非主流的256bit整数
- 不支持浮点数
- 缺乏标准库支持,例如字符串拼接、切割、查找等等都需要开发者自己实现
- 给合约打补丁或是部分升级合约代码在EVM中是完全不可能的
比特币的程序非常简单,由解锁脚本和锁定脚本构成。以太坊有智能合约,有图灵完备的虚拟机EVM,但是指令也相对简单,且自成一套。这两种程序本质上都是脚本程序,即由程序翻译指令并执行,而不是由本地机器CPU读取指令并执行,效率不高。但选择解释性语言有它的合理性,就是他的高度兼容性,它对智能合约的执行设备(矿机)没有限制。
WASM是EOS的智能合约标准Web Assembly,简称WASM
- 可以由解释器执行,也可以编译成机器码后执行
- 一种中间代码(字节码),所有其他语言编写的程序(C \ C++ \ Rust \ Golang \ java 等)都可以编程成wasm字节码的程序
- Wasm 是分布式系统开发的基础,智能合约将能够用可以被编译成 wasm 的任何语言进行开发。
- 智能合约可升级
它是谷歌、苹果、微软三大竞争公司同时支持的一种中间代码(字节码), 是浏览器都支持的一种代码。
WASM
基于EOS.IO的区块链使用Web Assembly(WASM)执行开发者提供的应用代码。WASM是一个已崭露头角的web标准,受到Google, Microsoft, Apple及其他大公司的广泛支持。目前为止,最成熟的用于构建应用及WASM代码编译的工具链是clang/LLVM及其C/C++编译器。希望C++ 将成为开发高性能及安全智能合约的最佳语言
LLVM的命名最早来源于底层语言虚拟机(Low Level Virtual Machine)的缩写。它是一个用于建立编译器的基础框架,以C++编写。创建此工程的目的是对于任意的编程语言,利用该基础框架,构建一个包括编译时、链接时、执行时等的语言执行器。目前官方的LLVM只支持处理C/C++,Objective-C三种语言,当然也有一些非官方的扩展,使其支持ActionScript、Ada、D语言、Fortran、GLSL、Haskell、Java bytecode、Objective-C、Python、Ruby、Rust、Scala以及C#。
以太坊支持WASM吗?
- Parity 以太坊客户端在 Wasmi 解释器中运行 Wasm 字节码,以保证 Wasm 代码能够访问区块链并与区块链进行交互
- 可以在 Kovan 测试网(PoA算法,Parity专用,Rust 语言)上测试 Wasm;在该测试网上,EVM 智能合约以及 Wasm 智能合约可以共存甚至互动
EWASM
eWASM将允许以太坊开发人员能够使用多种编程语言来编写代码——而不仅仅目前所使用的以太坊专用的语言Solidity——据说eWASM还会带来大量的性能增强。
Ewasm (Web Assembly for Ethereum) 并不是一个智能合约语言,而是一个编译器目标,它将允许以太坊程序员用其他语言编程(如Rust,C ++,也许某一天是智能合约特定语言,如 Simplicity),并编译成以太坊风格的 WebAssembly。Ewasm 是 WebAssembly 的一个更安全的子集,它是 Web 平台相对较新的低级编译目标。方便的是,wasm(以及 ewasm)模块可以在任何 JavaScript 项目中使用。对于大多数区块链代码,通常超过 75% 的代码根本不在智能合约中 — 它在 JavaScript 中必须与智能合约进行通信。Ewasm 和 JavaScript 共享绑定和模块支持的共同基础。
实现思路
Cosmos-SDK
实现的Ethereum
的智能合约链,底层提供了EVM、Wasm两种虚拟机来运行智能合约。开发者可以使用传统的Solidity语言编写合约、或其它可以编译为WebAssembly
高级语言(如:Rust/C++/AssemblyScript
)编写合约,将编译后的合约字节码上传至 Chain
上运行;并且两种类型的字节码合约之间可以互相进行调用。
基于上述这种开发理念,引申出一个问题:如何在Wasm上实现实现Ethereum
. 对此初步提供了三种方案:
-
将Solidity合约编译为Wasm字节码
- 使用类似于SOLL的方案,将Solidity合约编译为等价的
WebAssembly
字节码,底层提供Wasm虚拟机来运行合约。
- 使用类似于SOLL的方案,将Solidity合约编译为等价的
-
在Wasm上提供系统合约,来运行Solidity字节码的合约
- 在Wasm虚拟机上构建EVM字节码器,来运行编译后的Solidity字节码。
-
提供两种虚拟机的实现,不同的字节码合约在不同的虚拟机上运行
- 使用两种虚拟机,分别运行
EVM字节码
合约和WebAseembly字节码
合约,同时在Wasm虚拟机上通过Host函数,提供Ethereum
的实现,以便做到两种虚拟机合约之间的互相调用。
- 使用两种虚拟机,分别运行
基于技术实现难度、运行性能等方面的考虑,采用了第三种方案。下面将详细介绍该方案的实现思路
Wasm虚拟机实现Ethereum
在Wasm虚拟机中运行合约时,除了虚拟机自身之外,还需要依赖一些外部Host函数的支持,通过在Host中,提供EEI实现,以便在Wasm虚拟机中提供 Ethereum
Chain提供了便于合约导入的SDK,以便在用高级语言(Rust/AssemblyScript/)写Wasm合约时,可以调用这些外部Host函数;将合约编译为Wasm字节码时,SDK中声明的所有export
函数,都将由Chain客户端提供实现。
示例如下:
在smart-sdk-as项目中,提供了as项目使用的sdk.
// cesi.ts
export declare function getCallDataSize(): u32
export declare function callDataCopy(bufOut: Uint8Array, offset: u32, length: u32): void
export declare function getCodeSize(): u32
// lib_contract.ts
export namespace contract {
export function getCodeSize(): u32 {
return cesi.getCodeSize();
}
....
}
// index_test.ts
export function testCoreAPI(): void {
var codeSize = contract.getCodeSize();
}
在smart chain链的客户端中,实现了这些Host函数
//export cesiGetCallDataSize
func cesiGetCallDataSize(context unsafe.Pointer) int32 {
return convertContext(context).getCallDataSize()
}
//export cesiCallDataCopy
func cesiCallDataCopy(context unsafe.Pointer, resultOffset int32, dataOffset, length int32) {
convertContext(context).callDataCopy(resultOffset, dataOffset, length)
}
//export cesiGetCodeSize
func cesiGetCodeSize(context unsafe.Pointer) int32 {
return convertContext(context).getCodeSize()
}
将用高级语言(Rust/AssemblyScript)编写的Wasm合约编译为字节码(WebAssembly ByteCode),上传至链,创建Wasm虚拟机后,进行运行,并由Host函数提供EEI语义的支持.
+--------------+ +---------------+ +---------------+
| | create VM | | run | |
| Wasm | ------------> | VM | ------------->| host func |
| ByteCode | | | | |
+--------------+ +---------------+ +---------------+
EVM虚拟机集成
在chain中,使用了[ethereum/evmone]作为以太坊虚拟机的实现,该项目内部实现了以太坊的所有指令;同时,提供了一个接口HostContext,负责与区块链进行数据交互;在smart chain 客户端中通过实现该接口,为evm虚拟机提供数据支持;
通过调用evmc提供的接口,从evmone的动态库中创建evm虚拟机;然后通过虚拟机提供的接口Execute,传入合约code,用户输入等一系列参数信息,运行合约。
+--------------+ +---------------+ +---------------+
| | params | contract code | run | |
| Load EMV | ------------> | input | ------------->| HostContext |
| | | ... | | |
+--------------+ +---------------+ +---------------+
虚拟机间的兼容
为了使smart chain在执行合约时,不用关注虚拟机的实现细节,增加了中间层,来兼容两种虚拟机的创建、执行;
- 在合约的元信息中增加标识,来表明合约字节码类型;
- 依据标识来创建指定类型的虚拟机;
- 通过引入中间层接口
ContractExecutor
,来屏蔽smart chain 在链上执行合约时,对两种虚拟机实现细节的关注. - 在虚拟机执行时,通过使用ABI编码后的参数,来做到虚拟机之间的互相调用
type ContractInfo struct {
ContractType uint8
}
func CreateVM(cfg Config) (ContractExecutor, error) {
switch cfg.Flag {
case EVM:
return evm.NewVmEvmc()
case Wasm:
return wasm.NewWasmer(cfg.Ctx, cfg.Keeper, cfg.ContractAddr, cfg.Code, cfg.CodeHash, cfg.Ims)
}
return nil, types.ErrVMUnsupported
}
type ContractExecutor interface {
Execute(ctx interface{}, rev evm.Revision,
kind evm.CallKind, static bool, depth int, gas int64,
destination sdk.AccAddress, sender sdk.AccAddress, input []byte, value int64,
code []byte, create2Salt []byte) (output []byte, gasLeft int64, err error)
}
+--------------+ +---------------+ +---------------+
| | create vm | | run | |
| Contract | ------------> | Contract | ------------->| Execute |
| Info | | Executor | | |
+--------------+ +---------------+ +---------------+
总结
至此,我们完整介绍了方案三(提供两种虚拟机)的实现思路;通过在链上支持两种虚拟机,来使以太坊APP的开发者可以几乎无成本的进行生态迁移。