---
# System prepended metadata

title: WASM 详细设计文档

---

# WASM 详细设计文档

## 模块组成
`WASM`（WebAssembly）是一种新的字节码格式，是一种全新的底层二进制语法，经编译器编译后的指令代码。
其体积小、可移植、加载快并兼容WEB的全新格式，是一种以安全有效的方式运行可移植程序的新技术。
`WASM`模块可以通过启动`WASM`虚拟机来执行`.wasm`字节码。`WASM`合约可以使用`Golang`, `Rust` 和 `C++` 语言编写， 通过编译工具最终编译成可供`WASM`虚拟机运行的`.wasm`字节码。

外部程序引用wasmvm的功能入口主要在 github.com/cosmwasm/wasmvm/lib.go 中的相关方法，主要方法有：NewVM，Create，Instantiate，Execute，Query 等涉及合约完整生命周期的操作。

## 详细流程设计
WASM的模块处理流程设计如下：
```mermaid
graph TD
start([客户端])--发送请求--> chain(区块链网络)
chain --解析消息--> router(路由器)
router --路由消息--> wasm(WASM模块)
wasm --Tx交易--> TX_Handler(TX Handler)
wasm --Query请求--> Query_Handler(Query Handler)

```
客户端发起交易会由链路由到`WASM`模块，`WASM`再根据交易类型来判断交给哪个处理器进行处理，如果是Tx交易的话会交由TX Handler进行处理，如果是查询交易的话会由Query Handler进行处理。

一笔合约交易的执行时序图如下：
```mermaid
sequenceDiagram
   participant Client
   participant Handler
   participant MsgServer
   participant Keeper
   participant WasmerEngine
   participant KVStore
   
   Client->>Handler:Txs
   Handler->>MsgServer:ExecuteContract
   MsgServer->>Keeper:Execute
   Keeper->>WasmerEngine:Execute
   WasmerEngine->>WasmerEngine:Execute
   WasmerEngine->>KVStore:Update
```

### 上传合约代码
```mermaid
graph TD
start([开始]) --> check(合约创建者不能为空且有创建合约的权限)
check --> work(解压缩合约代码<br>调用VM创建合约代码<br>分析合约代码格式<br>增加当前合约ID索引)
work --> store(创建合约信息并存储到数据库)
store --> returns(返回合约代码ID)


```
1. 用户发起上传编译好的合约代码交易
2. 检查交易：合约创建者不能为空且有创建合约的权限
3. 解压缩合约代码，调用VM创建合约代码，分析合约代码格式
4. 创建合约信息并存储到数据库
5. 返回合约代码ID

### 实例化合约
```mermaid
graph TD
start([开始]) --> createAddr(根据合约代码ID创建合约地址)
createAddr --> createAcc(根据合约地址创建合约账户并将其存到账户系统)
createAcc --> getCode(从数据库获取合约代码)
getCode --> params(使用示例化参数和合约代码封装成合约示例化交易)
params --> instantiate(使用VM处理示例化交易)
instantiate --> store(创建合约信息结构并存储到数据库)
store --> returns(返回合约地址和实例化合约的返回值)


```
1. 用户发起示例化合约的交易，参数包括合约代码ID，交易发起者，实例化合约的参数
2. 根据合约代码ID创建合约地址，并检查合约地址是否已经存在链上了，如果存在则报错
3. 根据合约地址创建合约账户并将其存到账户系统
4. 从数据库获取合约代码
5. 使用示例化参数和合约代码封装成合约示例化交易
6. 使用VM处理示例化交易
7. 创建合约信息结构并存储到数据库
8. 返回合约地址和实例化合约的返回值

### 调用合约函数
```mermaid
graph TD
start([开始]) --> getCodeInfo(根绝合约地址获取合约代码信息)
getCodeInfo --> params(封装合约执行参数<br>包括合约执行者,合约执行参数,合约地址,合约代码哈希)
params --> execute(调用VM执行合约)
execute --> returns(返回合约执行信息)

```

1. 用户发起执行合约的交易，参数包括合约地址和执行合约的参数
2. 根绝合约地址获取合约代码信息
3. 封装合约执行参数，包括合约执行者，合约执行参数，合约地址，合约代码哈希
4. 调用VM执行合约
5. 返回合约执行信息

### 合约查询
```mermaid
graph TD
start([开始]) --> params(根绝请求类型和参数拼接查询路径)
params --> returns(根绝查询路径查询链上合约信息)

```
1. 用户发起查询请求，查询请求参数
2. 根绝请求类型和参数拼接查询路径
3. 根绝查询路径查询链上合约信息

## 数据结构设计
### Keeper数据结构
`Keeper`用来管理数据的存储：
```golang=

// Keeper will have a reference to Wasmer with it's own data directory.
type Keeper struct {
	storeKey      sdk.StoreKey
	cdc           codec.BinaryCodec
	accountKeeper types.AccountKeeper
	//bank                  CoinTransferrer
	bank                  types.BankKeeper
	portKeeper            types.PortKeeper
	capabilityKeeper      types.CapabilityKeeper
	wasmVM                types.WasmerEngine
	wasmVMQueryHandler    WasmVMQueryHandler
	wasmVMResponseHandler WasmVMResponseHandler
	messenger             Messenger
	// queryGasLimit is the max wasmvm gas that can be spent on executing a query with a contract
	queryGasLimit uint64
	paramSpace    paramtypes.Subspace
}
```

### 消息数据结构
#### 上传合约代码消息
发送交易消息：
```go=
// MsgStoreCode submit Wasm code to the system
type MsgStoreCode struct {
	// Sender is the that actor that signed the messages
	Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"`
	// WASMByteCode can be raw or gzip compressed
	WASMByteCode []byte `protobuf:"bytes,2,opt,name=wasm_byte_code,json=wasmByteCode,proto3" json:"wasm_byte_code,omitempty"`
	// InstantiatePermission access control to apply on contract creation,
	// optional
	InstantiatePermission *AccessConfig `protobuf:"bytes,5,opt,name=instantiate_permission,json=instantiatePermission,proto3" json:"instantiate_permission,omitempty"`
}
```

交易返回：
```go=
// MsgStoreCodeResponse returns store result data.
type MsgStoreCodeResponse struct {
	// CodeID is the reference to the stored WASM code
	CodeID uint64 `protobuf:"varint,1,opt,name=code_id,json=codeId,proto3" json:"code_id,omitempty"`
}
}
```

#### 实例化链码消息
发送交易消息：
```go=
// MsgInstantiateContract create a new smart contract instance for the given
// code id.
type MsgInstantiateContract struct {
	// Sender is the that actor that signed the messages
	Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"`
	// Admin is an optional address that can execute migrations
	Admin string `protobuf:"bytes,2,opt,name=admin,proto3" json:"admin,omitempty"`
	// CodeID is the reference to the stored WASM code
	CodeID uint64 `protobuf:"varint,3,opt,name=code_id,json=codeId,proto3" json:"code_id,omitempty"`
	// Label is optional metadata to be stored with a contract instance.
	Label string `protobuf:"bytes,4,opt,name=label,proto3" json:"label,omitempty"`
	// Msg json encoded message to be passed to the contract on instantiation
	Msg RawContractMessage `protobuf:"bytes,5,opt,name=msg,proto3,casttype=RawContractMessage" json:"msg,omitempty"`
	// Funds coins that are transferred to the contract on instantiation
	Funds github_com_loong_projects_erhai_core_types.Coins `protobuf:"bytes,6,rep,name=funds,proto3,castrepeated=github.com/loong-projects/erhai-core/types.Coins" json:"funds"`
}
```

交易返回：
```go=
// MsgInstantiateContractResponse return instantiation result data
type MsgInstantiateContractResponse struct {
	// Address is the bech32 address of the new contract instance.
	Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
	// Data contains base64-encoded bytes to returned from the contract
	Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
}

```

#### 执行合约消息
发送交易消息：
```go=
// MsgExecuteContract submits the given message data to a smart contract
type MsgExecuteContract struct {
	// Sender is the that actor that signed the messages
	Sender string `protobuf:"bytes,1,opt,name=sender,proto3" json:"sender,omitempty"`
	// Contract is the address of the smart contract
	Contract string `protobuf:"bytes,2,opt,name=contract,proto3" json:"contract,omitempty"`
	// Msg json encoded message to be passed to the contract
	Msg RawContractMessage `protobuf:"bytes,3,opt,name=msg,proto3,casttype=RawContractMessage" json:"msg,omitempty"`
	// Funds coins that are transferred to the contract on execution
	Funds github_com_loong_projects_erhai_core_types.Coins `protobuf:"bytes,5,rep,name=funds,proto3,castrepeated=github.com/loong-projects/erhai-core/types.Coins" json:"funds"`
}

```

消息返回：
```go=
// MsgExecuteContractResponse returns execution result data.
type MsgExecuteContractResponse struct {
	// Data contains base64-encoded bytes to returned from the contract
	Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
}
```

#### 查询合约信息请求
发送请求：
```go=
// QueryContractInfoRequest is the request type for the Query/ContractInfo RPC
// method
type QueryContractInfoRequest struct {
	// address is the address of the contract to query
	Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
}
```

请求返回：
```go=

// MsgExecuteContractResponse returns execution result data.
type MsgExecuteContractResponse struct {
	// Data contains base64-encoded bytes to returned from the contract
	Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
}


```

#### 查询合约历史请求
发送请求：
```go=
// QueryContractHistoryRequest is the request type for the Query/ContractHistory
// RPC method
type QueryContractHistoryRequest struct {
	// address is the address of the contract to query
	Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
	// pagination defines an optional pagination for the request.
	Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"`
}


```

请求返回：
```go=
// QueryContractHistoryResponse is the response type for the
// Query/ContractHistory RPC method
type QueryContractHistoryResponse struct {
	Entries []ContractCodeHistoryEntry `protobuf:"bytes,1,rep,name=entries,proto3" json:"entries"`
	// pagination defines the pagination in the response.
	Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"`
}

```

#### 查询合约代码请求
发送请求：
```go=

// QueryContractsByCodeRequest is the request type for the Query/ContractsByCode
// RPC method
type QueryContractsByCodeRequest struct {
	CodeId uint64 `protobuf:"varint,1,opt,name=code_id,json=codeId,proto3" json:"code_id,omitempty"`
	// pagination defines an optional pagination for the request.
	Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"`
}

```

请求返回：
```go=
// QueryContractsByCodeResponse is the response type for the
// Query/ContractsByCode RPC method
type QueryContractsByCodeResponse struct {
	// contracts are a set of contract addresses
	Contracts []string `protobuf:"bytes,1,rep,name=contracts,proto3" json:"contracts,omitempty"`
	// pagination defines the pagination in the response.
	Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"`
}

```

#### 查询所有合约状态请求
发送请求：
```go=

// QueryAllContractStateRequest is the request type for the
// Query/AllContractState RPC method
type QueryAllContractStateRequest struct {
	// address is the address of the contract
	Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
	// pagination defines an optional pagination for the request.
	Pagination *query.PageRequest `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"`
}
```

请求返回：
```go=
// QueryAllContractStateResponse is the response type for the
// Query/AllContractState RPC method
type QueryAllContractStateResponse struct {
	Models []Model `protobuf:"bytes,1,rep,name=models,proto3" json:"models"`
	// pagination defines the pagination in the response.
	Pagination *query.PageResponse `protobuf:"bytes,2,opt,name=pagination,proto3" json:"pagination,omitempty"`
}

```

#### 查询合约原始数据请求
发送请求：
```go=
// QueryRawContractStateRequest is the request type for the
// Query/RawContractState RPC method
type QueryRawContractStateRequest struct {
	// address is the address of the contract
	Address   string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
	QueryData []byte `protobuf:"bytes,2,opt,name=query_data,json=queryData,proto3" json:"query_data,omitempty"`
}
```

请求返回：
```go=

// QueryRawContractStateResponse is the response type for the
// Query/RawContractState RPC method
type QueryRawContractStateResponse struct {
	// Data contains the raw store data
	Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"`
}

```

#### 查询智能合约状态请求
发送请求：
```go=
// QuerySmartContractStateRequest is the request type for the
// Query/SmartContractState RPC method
type QuerySmartContractStateRequest struct {
	// address is the address of the contract
	Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
	// QueryData contains the query data passed to the contract
	QueryData RawContractMessage `protobuf:"bytes,2,opt,name=query_data,json=queryData,proto3,casttype=RawContractMessage" json:"query_data,omitempty"`
}
```

请求返回：
```go=
// QuerySmartContractStateResponse is the response type for the
// Query/SmartContractState RPC method
type QuerySmartContractStateResponse struct {
	// Data contains the json data returned from the smart contract
	Data RawContractMessage `protobuf:"bytes,1,opt,name=data,proto3,casttype=RawContractMessage" json:"data,omitempty"`
}

```


## 数据存储设计

### 存储数据类型
#### 代码信息
代码信息用来存储上传的合约代码的信息：
```go=

// CodeInfo is data for the uploaded contract WASM code
type CodeInfo struct {
	// CodeHash is the unique identifier created by wasmvm
	CodeHash []byte `protobuf:"bytes,1,opt,name=code_hash,json=codeHash,proto3" json:"code_hash,omitempty"`
	// Creator address who initially stored the code
	Creator string `protobuf:"bytes,2,opt,name=creator,proto3" json:"creator,omitempty"`
	// InstantiateConfig access control to apply on contract creation, optional
	InstantiateConfig AccessConfig `protobuf:"bytes,5,opt,name=instantiate_config,json=instantiateConfig,proto3" json:"instantiate_config"`
}
```

#### 合约信息
合约信息用来存储WASM合约实例：
```go=

// ContractInfo stores a WASM contract instance
type ContractInfo struct {
	// CodeID is the reference to the stored Wasm code
	CodeID uint64 `protobuf:"varint,1,opt,name=code_id,json=codeId,proto3" json:"code_id,omitempty"`
	// Creator address who initially instantiated the contract
	Creator string `protobuf:"bytes,2,opt,name=creator,proto3" json:"creator,omitempty"`
	// Label is optional metadata to be stored with a contract instance.
	Label string `protobuf:"bytes,3,opt,name=label,proto3" json:"label,omitempty"`
	// Created Tx position when the contract was instantiated.
	// This data should kept internal and not be exposed via query results. Just
	// use for sorting
	Created *AbsoluteTxPosition `protobuf:"bytes,4,opt,name=created,proto3" json:"created,omitempty"`
	// Extension is an extension point to store custom metadata within the
	// persistence model.
	Extension *types.Any `protobuf:"bytes,5,opt,name=extension,proto3" json:"extension,omitempty"`
}
```



## 接口设计

### Keeper接口
`Keeper`是对外暴露的有关模块的操作接口：
```golang=
// decoratedKeeper contains a subset of the wasm keeper that are already or can be guarded by an authorization policy in the future
type decoratedKeeper interface {
	create(ctx sdk.Context, 
    creator sdk.AccAddress, 
    wasmCode []byte, 
    instantiateAccess *types.AccessConfig, 
    authZ AuthorizationPolicy) (codeID uint64, err error)
    
	instantiate(ctx sdk.Context, codeID uint64, 
    creator, admin sdk.AccAddress, initMsg []byte, 
    label string, deposit sdk.Coins, 
    authZ AuthorizationPolicy) (sdk.AccAddress, []byte, error)

	execute(ctx sdk.Context, contractAddress sdk.AccAddress, 
    caller sdk.AccAddress, msg []byte, coins sdk.Coins) ([]byte, error)
    
	setContractInfoExtension(ctx sdk.Context, contract 
    sdk.AccAddress, extra types.ContractInfoExtension) error
}
```

### 合约引擎接口
合约的执行引擎的接口如下：
```go=

// WasmerEngine defines the WASM contract runtime engine.
type WasmerEngine interface {

	// Create will compile the wasm code, and store the resulting pre-compile
	// as well as the original code. Both can be referenced later via CodeID
	// This must be done one time for given code, after which it can be
	// instatitated many times, and each instance called many times.
	//
	// For example, the code for all ERC-20 contracts should be the same.
	// This function stores the code for that contract only once, but it can
	// be instantiated with custom inputs in the future.
	Create(code wasmvm.WasmCode) (wasmvm.Checksum, error)

	// AnalyzeCode will statically analyze the code.
	// Currently just reports if it exposes all IBC entry points.
	AnalyzeCode(checksum wasmvm.Checksum) (*wasmvmtypes.AnalysisReport, error)

	// Instantiate will create a new contract based on the given codeID.
	// We can set the initMsg (contract "genesis") here, and it then receives
	// an account and address and can be invoked (Execute) many times.
	//
	// Storage should be set with a PrefixedKVStore that this code can safely access.
	//
	// Under the hood, we may recompile the wasm, use a cached native compile, or even use a cached instance
	// for performance.
	Instantiate(
		checksum wasmvm.Checksum,
		env wasmvmtypes.Env,
		info wasmvmtypes.MessageInfo,
		initMsg []byte,
		store wasmvm.KVStore,
		goapi wasmvm.GoAPI,
		querier wasmvm.Querier,
		gasMeter wasmvm.GasMeter,
		gasLimit uint64,
		deserCost wasmvmtypes.UFraction,
	) (*wasmvmtypes.Response, uint64, error)

	// Execute calls a given contract. Since the only difference between contracts with the same CodeID is the
	// data in their local storage, and their address in the outside world, we need no ContractID here.
	// (That is a detail for the external, sdk-facing, side).
	//
	// The caller is responsible for passing the correct `store` (which must have been initialized exactly once),
	// and setting the env with relevant info on this instance (address, balance, etc)
	Execute(
		code wasmvm.Checksum,
		env wasmvmtypes.Env,
		info wasmvmtypes.MessageInfo,
		executeMsg []byte,
		store wasmvm.KVStore,
		goapi wasmvm.GoAPI,
		querier wasmvm.Querier,
		gasMeter wasmvm.GasMeter,
		gasLimit uint64,
		deserCost wasmvmtypes.UFraction,
	) (*wasmvmtypes.Response, uint64, error)

	// Query allows a client to execute a contract-specific query. If the result is not empty, it should be
	// valid json-encoded data to return to the client.
	// The meaning of path and data can be determined by the code. Path is the suffix of the abci.QueryRequest.Path
	Query(
		code wasmvm.Checksum,
		env wasmvmtypes.Env,
		queryMsg []byte,
		store wasmvm.KVStore,
		goapi wasmvm.GoAPI,
		querier wasmvm.Querier,
		gasMeter wasmvm.GasMeter,
		gasLimit uint64,
		deserCost wasmvmtypes.UFraction,
	) ([]byte, uint64, error)


	// GetCode will load the original wasm code for the given code id.
	// This will only succeed if that code id was previously returned from
	// a call to Create.
	//
	// This can be used so that the (short) code id (hash) is stored in the iavl tree
	// and the larger binary blobs (wasm and pre-compiles) are all managed by the
	// rust library
	GetCode(code wasmvm.Checksum) (wasmvm.WasmCode, error)

```
`WasmerEngine`接口定义了完整的合约生命周期。

`WasmerEngine` 是WASM合约执行引擎，对上层模块`Keeper`提供接口,上层对合约的调用最终都会调用`WasmerEngine`对应接口进行操作。


由于`WASM VM`是由`Rust`代码编写的，`Go`无法直接调用`Rust`的代码，且虚拟机如果要使用`Go`中定义`KV Store`更新合约数据也需要调用`Go`中的代码。所以需要一个粘合剂来将两者连接起来，这个粘合剂就是`C`语言。
`Rust`编写的`WASM VM`会被编译成静态链接库文件，向外暴露虚拟机的接口实现，同时需要用`C`语言来定义这些接口，然后`Go`语言在调用虚拟机的接口时就可以直接调用`C`语言中定义的接口，它会自动链接到静态链接库文件中，从而实现了`Go`语言调用`Rust`语言实现的功能。
`C`中定义`Rust`中的接口如下：
```go=
// api.h
struct cache_t *init_cache(struct ByteSliceView data_dir,
                           struct ByteSliceView supported_features,
                           uint32_t cache_size,
                           uint32_t instance_memory_limit,
                           struct UnmanagedVector *error_msg);

struct UnmanagedVector save_wasm(struct cache_t *cache,
                                 struct ByteSliceView wasm,
                                 struct UnmanagedVector *error_msg);
struct UnmanagedVector instantiate(struct cache_t *cache,
                                   struct ByteSliceView checksum,
                                   struct ByteSliceView env,
                                   struct ByteSliceView info,
                                   struct ByteSliceView msg,
                                   struct Db db,
                                   struct GoApi api,
                                   struct GoQuerier querier,
                                   uint64_t gas_limit,
                                   bool print_debug,
                                   uint64_t *gas_used,
                                   struct UnmanagedVector *error_msg);

struct UnmanagedVector execute(struct cache_t *cache,
                               struct ByteSliceView checksum,
                               struct ByteSliceView env,
                               struct ByteSliceView info,
                               struct ByteSliceView msg,
                               struct Db db,
                               struct GoApi api,
                               struct GoQuerier querier,
                               uint64_t gas_limit,
                               bool print_debug,
                               uint64_t *gas_used,
                               struct UnmanagedVector *error_msg);

struct AnalysisReport analyze_code(struct cache_t *cache,
                                   struct ByteSliceView checksum,
                                   struct UnmanagedVector *error_msg);

```

### 合约数据存储接口
虚拟机在执行合约的过程中需要更新数据库，数据库是在`Go`中定义的，所以`Rust`需要调用`Go`中的代码，和`Go`调用`Rust`的过程正好相反。用`C`语言编写调用`Go`中数据库的接口，同时`Rust`中将调用数据库的函数定义成一个外部`C`语言实现的函数，这样`Rust`在调用数据库读写函数时首先调用外部`C`语言定义的接口，改接口的实现实际上是在`Go`代码中，这就完成了`Rust`调用`Go`代码的功能。
`C`语言定义数据库读写的接口如下:
```go=
// Gateway functions (db)
GoResult cGet_cgo(db_t *ptr, gas_meter_t *gas_meter, 
                  uint64_t *used_gas, U8SliceView key,
                  UnmanagedVector *val, UnmanagedVector *errOut) {
	return cGet(ptr, gas_meter, used_gas, key, val, errOut);
}
GoResult cSet_cgo(db_t *ptr, gas_meter_t *gas_meter, 
                  uint64_t *used_gas, U8SliceView key, 
                  U8SliceView val, UnmanagedVector *errOut) {
	return cSet(ptr, gas_meter, used_gas, key, val, errOut);
}
GoResult cDelete_cgo(db_t *ptr, gas_meter_t *gas_meter, 
                     uint64_t *used_gas, U8SliceView key, 
                     UnmanagedVector *errOut) {
	return cDelete(ptr, gas_meter, used_gas, key, errOut);
}
GoResult cScan_cgo(db_t *ptr, gas_meter_t *gas_meter, 
                   uint64_t *used_gas, U8SliceView start, U8SliceView end, 
                   int32_t order, GoIter *out, UnmanagedVector *errOut) {
	return cScan(ptr, gas_meter, used_gas, start, end, order, out, errOut);
}
```