概述
Coinex Smart Chain是一条基于Cosmos-SDK
实现的Ethereum Semantic
的智能合约链,底层提供了EVM、Wasm两种虚拟机来运行智能合约。开发者可以使用传统的Solidity语言编写合约、或其它可以编译为WebAssembly
高级语言(如:Rust/C++/AssemblyScript
)编写合约,将编译后的合约字节码上传至Coinex Smart Chain
上运行;并且两种类型的字节码合约之间可以互相进行调用。
基于上述这种开发理念,引申出一个问题:如何在Wasm上实现实现Ethereum Semantic
. 对此初步提供了三种方案:
-
将Solidity合约编译为Wasm字节码
- 使用类似于SOLL的方案,将Solidity合约编译为等价的
WebAssembly
字节码,底层提供Wasm虚拟机来运行合约。
- 使用类似于SOLL的方案,将Solidity合约编译为等价的
-
在Wasm上提供系统合约,来运行Solidity字节码的合约
- 在Wasm虚拟机上构建EVM字节码器,来运行编译后的Solidity字节码。
-
提供两种虚拟机的实现,不同的字节码合约在不同的虚拟机上运行
- 使用两种虚拟机,分别运行
EVM字节码
合约和WebAseembly字节码
合约,同时在Wasm虚拟机上通过Host函数,提供Ethereum Semantic
的实现,以便做到两种虚拟机合约之间的互相调用。
- 使用两种虚拟机,分别运行
基于技术实现难度、运行性能等方面的考虑,采用了第三种方案。下面将详细介绍该方案的实现思路
Wasm虚拟机实现Ethereum Semantic
在Wasm虚拟机中运行合约时,除了虚拟机自身之外,还需要依赖一些外部Host函数的支持,通过在Host中,提供EEI实现,以便在Wasm虚拟机中提供 Ethereum Semantic
.
Coinex Smart Chain提供了便于合约导入的SDK,以便在用高级语言(Rust/AssemblyScript/)写Wasm合约时,可以调用这些外部Host函数;将合约编译为Wasm字节码时,SDK中声明的所有export
函数,都将由Coinex Smart 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虚拟机集成
在coinex smart 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的开发者可以几乎无成本的进行生态迁移。