Ethereumのスマートコントラクト教材をHyperledger Fabric上でやってみる

昨年に続いて2回めの挑戦です。よろしくお願いします。

Hyperledger Fabric v1.3の新規機能の1つでもあるEVMなChaincodeを使って、Hyperledger Fabric上でSolidityで書いたEthereumのスマートコントラクト教材の定番らしいCryptoZombiesというDappなゲームアプリを動かしてみたいと思う。

# はじめに
以下のようなバックグラウンドです。

* Hyperledger Fabricには日頃から触れている
    * Hyperledger Explorerのコミュニティで活動中
* でもEthereumについてはさっぱり。Solidityでスマートコントラクトを書いたこともない

まずは以下に情報があるので、その手順に従って環境を作っていくことにする。

* https://github.com/hyperledger/fabric-chaincode-evm/blob/master/examples/EVM_Smart_Contracts.md

環境は

* Ubuntu 16.04.5 LTS
* docker 18.06.1-ce
* go version go1.11.1 linux/amd64

# 結論

EVM上で未サポートなオペコードに遭遇して、結局GradesDappの実行までには至らなかった。Readmeで取り上げられている [Simple Storage Dapp](https://solidity.readthedocs.io/en/v0.4.24/introduction-to-smart-contracts.html) は動いたので、現段階ではスマートコントラクトの実装によって動く、動かないが左右されそう。複雑度の高いスマートコントラクトでは動かない可能性が高いのではないか、という予測。なので、残念ながら、より複雑そうな CryptoZombies は試してもいない。

EthereumのDAppは、パブリックなチェーンでの利用シーンが主であると個人的には認識しているので、コンソーシアムなチェーンへのスマートコントラクトの流用がどれだけの恩恵をもたらすのか、今の時点でピンとこないのが正直なところですが、今後実装が充実すれば、Hyperledger Fabricの利用シーンを増やす1つの材料になるのかもしれない。

# まずはブロックチェーンネットワークを立ち上げる

~~ここはみなさんおなじみのfabric-samplesリポジトリのクローンから。~~

* 出だしから間違えたようです。詳しくは [こちら](#誤りポイント) を参照

```
git clone https://github.com/hyperledger/fabric-samples.git -b release-1.3
cd fabric-samples
./scripts/bootstrap.sh
export PATH=$PWD/bin:$PATH
```

ここまでで必要なコンテナイメージやネットワーク立ち上げに必要なツールがダウンロードされ、ツールにはパスを通した状態になる。

    cd first-network
    ./byfn.sh up

あとは上記を実行することで以下のログが出力されてネットワーク立ち上げ成功。

```
========= All GOOD, BYFN execution completed =========== 


 _____   _   _   ____   
| ____| | \ | | |  _ \  
|  _|   |  \| | | | | | 
| |___  | |\  | | |_| | 
|_____| |_| \_| |____/  

```

# EVM chaincodeをインストールする

~~ネットワークの準備が整ったところでスマートコントラクト実行に必要なリソースを取得する。~~

* `go get`で取得する必要がある。詳しくは [こちら](#誤りポイント) を参照

```
git clone https://github.com/hyperledger/fabric-chaincode-evm.git
```

EVMなchaincodeを取り込むためにdocker-composeファイルを以下の通り、少し修正する。

```diff
diff --git a/first-network/docker-compose-cli.yaml b/first-network/docker-compose-cli.yaml
index c7f7471..8995be3 100644
--- a/first-network/docker-compose-cli.yaml
+++ b/first-network/docker-compose-cli.yaml
@@ -83,6 +83,7 @@ services:
         - ./crypto-config:/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/
         - ./scripts:/opt/gopath/src/github.com/hyperledger/fabric/peer/scripts/
         - ./channel-artifacts:/opt/gopath/src/github.com/hyperledger/fabric/peer/channel-artifacts
+        - ./../../fabric-chaincode-evm:/opt/gopath/src/github.com/hyperledger/fabric-chaincode-evm
     depends_on:
       - orderer.example.com
       - peer0.org1.example.com
```

ちなみにここでは以下のようなディレクトリ構成で話をすすめる。

```
$GOPATH/src/github.com/hyperledger/
   fabric-samples
   fabric-samples/first-network
   fabric-chaincode-evm
```

これにより、cliコンテナからEVM chaincodeを見えるようになり、任意のPeer上にcliコンテナからインストール可能になる。

* ちなみにcliコンテナの役割については以下のトピックがわかりやすい
    * https://stackoverflow.com/questions/46731930/hyperledger-fabric-cli-docker-container

ブロックチェーンネットワークを再起動する

```
cd fabric-samples/first-network
./byfn.sh down
./byfn.sh up
```

以下のスクリプト `scripts/installevm.sh` を作成し、cliコンテナ上から実行してEVM chaincodeをpeer0.org1ノードにインストールする


```bash:scripts/installevm.sh
#!/bin/bash
export CORE_PEER_MSPCONFIGPATH=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/users/Admin@org1.example.com/msp
export CORE_PEER_ADDRESS=peer0.org1.example.com:7051
export CORE_PEER_LOCALMSPID="Org1MSP"
export CORE_PEER_TLS_ROOTCERT_FILE=/opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt
peer chaincode install -n evmcc -l golang -v 0 -p github.com/hyperledger/fabric-chaincode-evm/evmcc
peer chaincode instantiate -n evmcc -v 0 -C mychannel -c '{"Args":[]}' -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
```

```
cd fabric-samples/first-network
vi scripts/installevm.sh
chmod a+x scripts/installevm.sh
docker exec -it cli scripts/installevm.sh
```

ここまでエラーが出なければ、`dev-peer0.org1.example.com-evmcc-0`という新しいコンテナがスタートしているはず。
ためしに何かスマートコントラクトをbytecode直指定でデプロイしてみる。

```
docker exec -it cli peer chaincode invoke -n evmcc -C mychannel  -c '{"Args":["0000000000000000000000000000000000000000","608060405234801561001057600080fd5b5060df8061001f6000396000f3006080604052600436106049576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806360fe47b114604e5780636d4ce63c146078575b600080fd5b348015605957600080fd5b5060766004803603810190808035906020019092919050505060a0565b005b348015608357600080fd5b50608a60aa565b6040518082815260200191505060405180910390f35b8060008190555050565b600080549050905600a165627a7a723058203dbaed52da8059a841ed6d7b484bf6fa6f61a7e975a803fdedf076a121a8c4010029"]}' -o orderer.example.com:7050 --tls --cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
2018-12-11 12:48:51.579 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 001 Chaincode invoke successful. result: status:200 payload:"38a9177411170d69038cdabb3758507d33f1cbb0" 
```

成功したっぽい。 :smile: 

# もっと初歩のGradesDappを動かすことにする

あまりにもSolidityによるスマートコントラクトのデプロイ方法やアクセス方法のことを知らないので、もう１ステップ踏む。以下の成績表アプリGradesDappを動かしてみることにする。

[A Beginner’s Guide to Blockchain Programming – Hacker Noon](https://hackernoon.com/a-beginners-guide-to-blockchain-programming-4913d16eae31?gi=1700f6738b21)

まずEthereum環境で動かすわけだが、そこは端折ることにする。言われるがままにやればよかった。以下のポイントにだけ注意する。

### ポイント1
* solcのバージョンが新しいとコンパイルに失敗する。ので、solcのバージョンを明に指定した(`^0.4.18`)。

# バイトコードをEVM chaincode上にデプロイ

Ethereum環境で試す過程でコントラクトのバイトコードも手に入ったので、さっそくHyperledger Fabric上のEVM chaincodeにデプロイする。

```bash:scripts/deploy_contract.sh
#!/bin/bash
docker exec -it cli peer chaincode invoke -n evmcc -C mychannel \
-c '
  {
    "Args":[
      "0000000000000000000000000000000000000000",
      "[ contract の 長いバイトコードを貼り付ける ]"
    ]
  }
  ' \
-o orderer.example.com:7050 \
--tls \
--cafile /opt/gopath/src/github.com/hyperledger/fabric/peer/crypto/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem
```

```
cd $GOPAH/src/github.com/hyperledger/fabric-samples/first-network
scripts/deploy_contract.sh
2018-12-13 14:16:48.856 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 001 Chaincode invoke successful. result: status:200 payload:"6f0c0e4cc87dd1c52204016bc6c26128294d025b" 
```

成功! :smiley: contract address `6f0c0e4cc87dd1c52204016bc6c26128294d025b` がトランザクションの返却値として得られる。

さっそくスマートコントラクトにアクセスしてみたいのだが、これがひときわ面倒。関数のハッシュ求めて、引数をHexに変換して関数ハッシュと結合して、、となるわけだ。
関数ハッシュはコンパイルした時の出力にあったが（以下、ポイント2参照)、引数指定とか果てしなく面倒なので、ここでようやくweb3.jsの登場なのである。

### ポイント2

* function hashはコンパイルしたコードの中に格納されている

```
funchash = compiledCode.contracts[':Grades'].functionHashes
{ 'getGradeForStudent(bytes32)': 'ed12bfb5',
  'giveGradeToStudent(bytes32,string)': 'ab258bab',
  'grades(bytes32)': '304823e5',
  'studentList(uint256)': '7df221e9',
  'validStudent(bytes32)': '34e47d50' }
```

# RPCでEVMで会話してくれるfabproxyを動かす

fabric-chaincode-evm内に含まれる fabproxy をビルドする必要があるのだが少し手間だった。 :frowning2: 


* クローンした `fabric-chaincode-evm` ディレクトリをGOPATHが通ったところに移したり（ or GOPATHに追加したり)
* 足りないパッケージを`go build`時のエラーメッセージに従ってコツコツインストールしたり ( スマートな依存関係解決方法がありそうな気もするがGOの日が浅いのでまだ深く追わないことにする )
* ビルドエラーが出たり

```
fabproxy/cmd/main.go:73:38: cannot use client (type *"github.com/hyperledger/fabric-sdk-go/pkg/client/channel".Client) as type fabproxy.ChannelClient in argument to fabproxy.NewEthService:
	*"github.com/hyperledger/fabric-sdk-go/pkg/client/channel".Client does not implement fabproxy.ChannelClient (wrong type for Execute method)
		have Execute("github.com/hyperledger/fabric-sdk-go/pkg/client/channel".Request, ..."github.com/hyperledger/fabric-sdk-go/pkg/client/channel".RequestOption) ("github.com/hyperledger/fabric-sdk-go/pkg/client/channel".Response, error)
		want Execute("github.com/hyperledger/fabric-chaincode-evm/vendor/github.com/hyperledger/fabric-sdk-go/pkg/client/channel".Request, ..."github.com/hyperledger/fabric-chaincode-evm/vendor/github.com/hyperledger/fabric-sdk-go/pkg/client/channel".RequestOption) ("github.com/hyperledger/fabric-chaincode-evm/vendor/github.com/hyperledger/fabric-sdk-go/pkg/client/channel".Response, error)
fabproxy/cmd/main.go:73:38: cannot use ledger (type *"github.com/hyperledger/fabric-sdk-go/pkg/client/ledger".Client) as type fabproxy.LedgerClient in argument to fabproxy.NewEthService:
	*"github.com/hyperledger/fabric-sdk-go/pkg/client/ledger".Client does not implement fabproxy.LedgerClient (wrong type for QueryBlock method)
		have QueryBlock(uint64, ..."github.com/hyperledger/fabric-sdk-go/pkg/client/ledger".RequestOption) (*"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common".Block, error)
		want QueryBlock(uint64, ..."github.com/hyperledger/fabric-chaincode-evm/vendor/github.com/hyperledger/fabric-sdk-go/pkg/client/ledger".RequestOption) (*"github.com/hyperledger/fabric-chaincode-evm/vendor/github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common".Block, error)
fabproxy/cmd/main.go:73:38: cannot use logger (type *"go.uber.org/zap".SugaredLogger) as type *"github.com/hyperledger/fabric-chaincode-evm/vendor/go.uber.org/zap".SugaredLogger in argument to fabproxy.NewEthService
```

~~っていうところで、日付も変わりそうなので明日以降に続く。。枠に空きはありそうなので~~

`fabric-chaincode-evm`をクローンしたのがいけなかった。`go get`で取得した上でビルドすると成功 :smiley: 

### 誤りポイント

* `fabric-chaincode-evm`は`go get`で取得する。fabric-samplesもこれに基づいて、GOPATH内に移動
    * ちなみにこの注意ポイントは間接的に [Readme](https://github.com/hyperledger/fabric-chaincode-evm/blob/master/examples/EVM_Smart_Contracts.md#setting-up-the-fab-proxy)  にも書いてあると言えなくもない(分かりにくいけど)。
    * 途中からリンクから飛んできた場合は元の説明に戻って手順を継続

```shell
go get github.com/hyperledger/fabric-chaincode-evm
cd $GOPATH/src/github.com/hyperledger/fabric-chaincode-evm
go build -o fab3 ./fabproxy/cmd
cd $GOPATH/src/github.com/hyperledger
git clone https://github.com/hyperledger/fabric-samples.git -b release-1.3
```
必要な環境変数を設定して生成した実行ファイル `fab3` を実行

```shell:fab3-envsetup.sh
# Environment Variables for Fabproxy:
export FABPROXY_CONFIG=${GOPATH}/src/github.com/hyperledger/fabric-chaincode-evm/examples/first-network-sdk-config.yaml # Path to a compatible Fabric SDK Go config file
export FABPROXY_USER=User1 # User identity being used for the proxy (Matches the users names in the crypto-config directory specified in the config)
export FABPROXY_ORG=Org1  # Organization of the specified user
export FABPROXY_CHANNEL=mychannel # Channel to be used for the transactions
export FABPROXY_CCID=evmcc # ID of the EVM Chaincode deployed in your fabric network
export PORT=5000 # Port the proxy will listen on. If not provided default is 5000.
```

```
$ source fab3-envsetup.sh
$ ./fab3
{"level":"info","ts":1544707161.6615047,"logger":"fab3","caller":"cmd/main.go:75","msg":"Starting Fab Proxy on port 5000\n"}
```

# nodeからweb3経由でコントラクトにアクセス

取得している成績表DAppである GradesDapp ディレクトリに移動し、指定の web3 パッケージをインストールした上で node のインタープリターを開く

```shell
npm install web3@0.20.2
node
>
```

以下を実行すると John に A+ をあげてみる。途中の結果出力は割愛。Solidityのコード`Grades.sol` と [デプロイの手順](#バイトコードをEVM chaincode上にデプロイ) で取得した contract address を参照する。

```
code = fs.readFileSync('Grades.sol').toString()
solc = require('solc')
compiledCode = solc.compile(code)

Web3 = require('web3')
web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:5000'))

web3.eth.accounts
web3.eth.defaultAccount = web3.eth.accounts[0]

abiDefinition = JSON.parse(compiledCode.contracts[':Grades'].interface)
GradesContract = web3.eth.contract(abiDefinition)
byteCode = compiledCode.contracts[':Grades'].bytecode

myContract = GradesContract.at('6f0c0e4cc87dd1c52204016bc6c26128294d025b')
myContract.giveGradeToStudent('John', 'A+', {from: web3.eth.accounts[0]})
Error: Failed to execute transaction: Transaction processing for endorser [localhost:7051]: Chaincode status Code: (500) UNKNOWN. Description: failed to execute contract: call error: REVERT not yet fully implemented
    at Object.InvalidResponse (/home/atsushi/qiita/02_advent_2018/GradesDapp/node_modules/web3/lib/web3/errors.js:38:16)
    at RequestManager.send (/home/atsushi/qiita/02_advent_2018/GradesDapp/node_modules/web3/lib/web3/requestmanager.js:61:22)
    at Eth.send [as sendTransaction] (/home/atsushi/qiita/02_advent_2018/GradesDapp/node_modules/web3/lib/web3/method.js:145:58)
    at SolidityFunction.sendTransaction (/home/atsushi/qiita/02_advent_2018/GradesDapp/node_modules/web3/lib/web3/function.js:170:26)
    at SolidityFunction.execute (/home/atsushi/qiita/02_advent_2018/GradesDapp/node_modules/web3/lib/web3/function.js:256:37)
```

`call error: REVERT not yet fully implemented` のエラー。なにやらEVMの実装上、GradesDappのバイトコード内で使っていると思われる `REVERT` オペコードがまだ未サポートな模様。。。

# リンク

参考にさせて頂きました。 :bow: 

* [hyperledger/fabric-chaincode-evm](https://github.com/hyperledger/fabric-chaincode-evm)
* [A Beginner’s Guide to Blockchain Programming – Hacker Noon](https://hackernoon.com/a-beginners-guide-to-blockchain-programming-4913d16eae31)
* [Goオフィシャルチーム作成の依存関係管理ツール dep を試してみた ｜ DevelopersIO](https://dev.classmethod.jp/go/dep/)