EthermintをforkしてeWASMを追加した

最近、（自分の中で）WebAssemblyがきてます。
自分が普段関わってるBlockchainの分野でもWebAssemblyの導入が多く議論されています。
その一つにEthereum上でwasmを動かすeWASM VMがあります。

開発が止まっているみたいですが、[testnet](https://github.com/ewasm/testnet)も用意されています。

これには、[hera](https://github.com/ewasm/hera)というeWASM VMが使われていますが、こちらもかなり前から開発が止まっているようでした...

さらに調査してみるとSecond StateというプロジェクトでWebAssemblyを使ったVMが開発されていることが分かりました。

![スクリーンショット 2020-06-08 23.58.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129706/5f908895-78a4-a19a-d869-3c733290c4a0.png)

Official: https://www.secondstate.io/
Github: https://github.com/second-state/SSVM

ということで、今回はeWASMとそれに関連したSecond Stateのプロジェクトについて調べてみました。
そして、最新のgo-ethereumをimportしてeWASMが使えるようにEthermintを改造してみました。

# eWASMについて
## Ewasm 1.0

- wasmのサブセット
- 各命令の前にuseGASを挿入して、GASのコストを計算する
- eWASM Contract
- 不動小数点を使わない
- Ethereum Environment Interfaceのモジュール のみをimportし、他のモジュール はimportしていない
- mainとmemoryという2つのシンボルをexportする
  - main: VMが実行する関数
  - memory: EEIのモジュール が書き込むメモリスペース
- Ethereum Environment Interface(EEI)
  - eWASM ContractがEthereumにアクセスするためのAPI
  - WebAssemblyのModuleとして実装され、eWASMの中でimportして使う
  - 例えば、Ethereumの特定のAccountの残高を取得するgetBalance, Accountにメッセージを送るcallなど
元々、EVM1の命令として実装されていたが、WASMではこのような命令はないので、モジュール としてimportして使う仕様となっている

- System Contract
  - eWASM VMが利用するContract
  - Contractとして定義されたインターフェース
  - 仮想マシンの外で実装したいロジックをContractの形で定義しておき、仮想マシンがcallして使う仕組み
  - EVM1のPrecompiled Contractに相当
  - Sentinel Contract
      - ContractをdeployするときにVMが呼び出し、
          1. コードがeWASMの仕様にあっている確認
          - gas計算ロジックを追加
          - deploy準備OKの印として、preambleをつける
  - EVM Transcompiler
      - EVM1のbytecodeをeWASMにtranscompileする処理を実行
      - EVM1をサポートしていない場合VMを呼び出す

## Ewasm 2.0
- Ewasm 2.0のSmart contractはExecution Environments (EE)と呼ばれる
- EEは全てWASMによって作成される
- クロスシャードをサポートするため、各EEはシャードで実行される
- EEはstate rootのみを取得できる
- EEはstateless
- scout: Ethereum 2.0 Phase 2 execution prototyping engine
  - https://github.com/ewasm/scout

- 詳細は今後調査したい。特にstate shardingの環境下でどうやってcontractを実行するのか...など

# Second stateのプロジェクトを使ったeWASMの構造
- eWASMが使えるVMは現在Second stateによって開発が進められている。

![ssvm-eth.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/129706/3cda8c71-1638-7d53-ef06-30a1546f52e4.png)

- evmc: Ethereum clientに接続するためのAPI
  - https://github.com/ethereum/evmc
- ssvm-evmc: EVMCのためのWebAssembly VM
  - https://github.com/second-state/ssvm-evmc
- SSVM: WebAssembly Virtual Machine, 必ずしもブロックチェーンのためのVMではない
  - https://github.com/second-state/SSVM

# ewasmint
調べたことを参考にEthermintをwasmで実行できるように改造してみました。

## go-ethereum
- 最新版のgo-ethereumはewasmのinterpreterに対応していないので、forkしてvm moduleを修正しました。

- EVMのinstanceを作るときにchainconfigを確認して、wasmのflagが立っていれば、ewasmのinterpreterを使うように分岐しています。
- &EVMC{...}でewasm用のinstanceを用意します。
  - https://github.com/shiki-tak/go-ethereum/blob/feature/dev-ewasm/core/vm/evmc.go

```golang:evm.go
func NewEVM(ctx Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *EVM {
	evm := &EVM{
		Context:      ctx,
		StateDB:      statedb,
		vmConfig:     vmConfig,
		chainConfig:  chainConfig,
		chainRules:   chainConfig.Rules(ctx.BlockNumber),
		interpreters: make([]Interpreter, 0, 1),
	}

	if chainConfig.IsEWASM(ctx.BlockNumber) {
		evm.interpreters = append(evm.interpreters, &EVMC{ewasmModule, evm, evmc.CapabilityEWASM, false})
	}

	if vmConfig.EVMInterpreter != "" {
		evm.interpreters = append(evm.interpreters, &EVMC{evmModule, evm, evmc.CapabilityEVM1, false})
	} else {
		// vmConfig.EVMInterpreter will be used by EVM-C, it won't be checked here
		// as we always want to have the built-in EVM as the failover option.
		evm.interpreters = append(evm.interpreters, NewEVMInterpreter(evm, vmConfig))
	}

	evm.interpreter = evm.interpreters[0]

	return evm
}
```

## Ethermint
- emintdをstartするときにssvm-evmcをbuildして生成されたbinaryを指定します。

```shell
emintd start --vm-wasm <your/build/folder>/tools/ssvm-evmc/libssvm-evmc.dylib
```
- このflagを利用してEVMのinstanceを作るときにwasmを選択するようにしています。
- go-ethereumでもwasmを使うときのflagが用意されています。それを指定するとvm.InitEVMCEwasmが呼ばれて、pathが設定されますが、Ethermintでは外部からvmのinstanceを作るようにしなければならないので、NewEVMの直前でvm.InitEVMCEwasmを読んでいます。


```golang:state_transition.go
func (st StateTransition) newEVM(ctx sdk.Context, csdb *CommitStateDB, gasLimit uint64, gasPrice *big.Int) *vm.EVM {
	// Create context for evm
	context := vm.Context{
		CanTransfer: core.CanTransfer,
		Transfer:    core.Transfer,
		Origin:      st.Sender,
		Coinbase:    common.Address{}, // there's no benefitiary since we're not mining
		BlockNumber: big.NewInt(ctx.BlockHeight()),
		Time:        big.NewInt(ctx.BlockHeader().Time.Unix()),
		Difficulty:  big.NewInt(0), // unused. Only required in PoW context
		GasLimit:    gasLimit,
		GasPrice:    gasPrice,
	}

	if emint.VM != "" {
		vm.InitEVMCEwasm(emint.VM)
		return vm.NewEVM(context, csdb, GenerateWASMChainConfig(st.ChainID), vm.Config{})
	}
	return vm.NewEVM(context, csdb, GenerateEVM1ChainConfig(st.ChainID), vm.Config{})

}
```


あとは、READMEにしたがって進めるとEthermint上でwasm contractが実行できます。
Solidityからwasmへのcompilerは[SOLL](https://github.com/second-state/SOLL)を使います。

コードは以下にあります。
Github: https://github.com/shiki-tak/ewasmint

# まとめ
- eWASMにも1.0と2.0がある。
- 1.0はEVMをwasmで実現した感じ？
- 2.0になるとShard chainに対応したVMになるみたい。
- Blockchain + WebAssemblyはまじ楽しい！
- 調べれば調べるほど、色々なプロジェクトが出てくるので今後も継続して調査したい