最近、(自分の中で)WebAssemblyがきてます。
自分が普段関わってるBlockchainの分野でもWebAssemblyの導入が多く議論されています。
その一つにEthereum上でwasmを動かすeWASM VMがあります。
開発が止まっているみたいですが、testnetも用意されています。
これには、heraというeWASM VMが使われていますが、こちらもかなり前から開発が止まっているようでした...
さらに調査してみるとSecond StateというプロジェクトでWebAssemblyを使ったVMが開発されていることが分かりました。
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が呼び出し、
- コードがeWASMの仕様にあっている確認
- gas計算ロジックを追加
- deploy準備OKの印として、preambleをつける
- ContractをdeployするときにVMが呼び出し、
- 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
-
詳細は今後調査したい。特にstate shardingの環境下でどうやってcontractを実行するのか...など
Second stateのプロジェクトを使ったeWASMの構造
- eWASMが使えるVMは現在Second stateによって開発が進められている。
- evmc: Ethereum clientに接続するためのAPI
- ssvm-evmc: EVMCのためのWebAssembly VM
- SSVM: WebAssembly Virtual Machine, 必ずしもブロックチェーンのためのVMではない
ewasmint
調べたことを参考にEthermintをwasmで実行できるように改造してみました。
go-ethereum
-
最新版のgo-ethereumはewasmのinterpreterに対応していないので、forkしてvm moduleを修正しました。
-
EVMのinstanceを作るときにchainconfigを確認して、wasmのflagが立っていれば、ewasmのinterpreterを使うように分岐しています。
-
&EVMC{...}でewasm用のinstanceを用意します。
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を指定します。
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を読んでいます。
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を使います。
コードは以下にあります。
Github: https://github.com/shiki-tak/ewasmint
まとめ
- eWASMにも1.0と2.0がある。
- 1.0はEVMをwasmで実現した感じ?
- 2.0になるとShard chainに対応したVMになるみたい。
- Blockchain + WebAssemblyはまじ楽しい!
- 調べれば調べるほど、色々なプロジェクトが出てくるので今後も継続して調査したい