毕设11 - 解耦 CosmWasm 模块

| 笔记 | 3571 | 9分钟 | 毕设CosmosCosmWasmwasmd

现在要理解 Cosmos 的运作机制,把 CosmWasm 模块解耦。即节点本身不执行 Wasm 智能合约,通过发送请求调用远程智能合约虚拟机,获取返回结果。

先重新跑一下 wasmd 。

1 重新跑 wasmd

1.1 修改客户端配置

~/.wasmd/config/client.toml 中设置了 wasmd 作为客户端的配置,将其修改为:

chain-id = "testing"
keyring-backend = "test"
output = "text"
node = "tcp://localhost:26657"
broadcast-mode = "sync"

1.2 运行脚本

setup_wasmd.sh

#!/bin/sh
#set -o errexit -o nounset -o pipefail

PASSWORD=${PASSWORD:-1234567890}
STAKE=${STAKE_TOKEN:-ustake}
FEE=${FEE_TOKEN:-ucosm}
CHAIN_ID=${CHAIN_ID:-testing}
MONIKER=${MONIKER:-node001}

wasmd init --chain-id "$CHAIN_ID" "$MONIKER"
# staking/governance token is hardcoded in config, change this
sed -i "s/\"stake\"/\"$STAKE\"/" "$HOME"/.wasmd/config/genesis.json
# this is essential for sub-1s block times (or header times go crazy)
sed -i 's/"time_iota_ms": "1000"/"time_iota_ms": "10"/' "$HOME"/.wasmd/config/genesis.json

if ! wasmd keys show validator; then
  (echo "$PASSWORD"; echo "$PASSWORD") | wasmd keys add validator
fi
# hardcode the validator account for this instance
echo "$PASSWORD" | wasmd genesis add-genesis-account validator "1000000000$STAKE,1000000000$FEE"

# (optionally) add a few more genesis accounts
for addr in "$@"; do
  echo $addr
  wasmd genesis add-genesis-account "$addr" "1000000000$STAKE,1000000000$FEE"
done

# submit a genesis validator tx
## Workraround for https://github.com/cosmos/cosmos-sdk/issues/8251
(echo "$PASSWORD"; echo "$PASSWORD"; echo "$PASSWORD") | wasmd genesis gentx validator "250000000$STAKE" --chain-id="$CHAIN_ID" --amount="250000000$STAKE"
## should be:
# (echo "$PASSWORD"; echo "$PASSWORD"; echo "$PASSWORD") | wasmd gentx validator "250000000$STAKE" --chain-id="$CHAIN_ID"
wasmd genesis collect-gentxs

run_wasmd.sh

#!/bin/sh

if test -n "$1"; then
    # need -R not -r to copy hidden files
    cp -R "$1/.wasmd" /root
fi

mkdir -p /root/log
wasmd start --rpc.laddr tcp://0.0.0.0:26657 --trace

运行:

./setup_wasmd.sh
./run_wasmd.sh

之后修改完代码后,重新编译:

cd path/to/wasmd
make install

2 分析代码

2.1 上传智能合约

wasmd tx wasm store simple_counter.wasm --from mywallet --gas 2000000 --fees 50000ustake

这段代码会向区块链发出一个 StoreCode 的事务。

当节点接收到这个事务后,会调用 wasmVM ,把智能合约缓存在本地。这个过程并不是在区块链节点的 Go 代码,而是虚拟机的 Rust 代码。核心代码:

pub fn save_wasm_unchecked(&self, wasm: &[u8]) -> VmResult<Checksum> {
    // We need a new engine for each Wasm -> module compilation due to the metering middleware.
    let compiling_engine = make_compiling_engine(None);
    // This module cannot be executed directly as it was not created with the runtime engine
    let module = compile(&compiling_engine, wasm)?;

    let mut cache = self.inner.lock().unwrap();
    let checksum = save_wasm_to_disk(&cache.wasm_path, wasm)?;
    cache.fs_cache.store(&checksum, &module)?;
    Ok(checksum)
}

最终结果是,节点所在的主机中会保存两个文件:

  1. 智能合约原始字节码(.wasm文件)

    位于 ~/.wasmd/wasm/wasm/state/wasm/<字节码的checksum>.wasm

  2. wasm 编译后的模块(module)

    位于 ~/.wasmd/wasm/wasm/cache/modules/5b35f8ce52-wasmer7/x86_64-unknown-linux-gnu-A91CB5FD/<字节码的checksum>.wasm

2.2 实例化智能合约

wasmd tx wasm instantiate 1 '{"count":0}' --from validator --label "Simple Counter" --gas 200000 --fees 5000ustake --admin $(junod keys show mywallet -a)

实例化,会在区块链上创建一个新的合约地址,之后通过这个地址与该合约交互。

合约的状态会存储在区块链的 KVStore 里,通过一个唯一前缀来划分专属这个合约实例的存储区域。

之后,会调用虚拟机执行智能合约的 instantiate 函数:(keeper.go 337

res, gasUsed, err := k.wasmVM.Instantiate(codeInfo.CodeHash, env, info, initMsg, vmStore, cosmwasmAPI, querier, k.gasMeter(sdkCtx), gasLeft, costJSONDeserialization)

WasmVM 由两部分组成:

  • go语言编写的转发API。

    k.wasmVM.Instantiate 调用的就是 go 语言编写的 API。这个API最终会把请求转发给 Rust 编写的函数。

  • libwasmvm,Rust 编写的虚拟机具体实现。这个例子里,就是 calls.rs 中的:

    pub extern "C" fn instantiate

libwasmvm 的执行逻辑:

  • 从 cache 中获取编译好的模块。

  • 将模块实例化(wasmer)。

  • 实例化后的模块可以执行内部的的函数:

    call_raw(
        instance,
        "instantiate",
        &[env, info, msg],
        read_limits::RESULT_INSTANTIATE,
    )

推进不下去

直接分离虚拟机这一步跨度太大,已经好几天下不去手了。

先试试直接在虚拟机里运行Wasm智能合约。可以获取一些性能数据。

然后再想办法把虚拟机移植到TEE里。获取一些性能数据。

最后一步,才是把节点在区块链上跑起来,然后套之前的模板,实现零知识证明的验证。