【2019年3月版】Hyperledger fabric の EVM を試す。～ Hyperledger Burrow入門 ～

# Hyperledger Fabric で Ethereum のスマートコントラクトが動くらしい。

先日、以下の記事を見つけました。
[Ethereum のスマートコントラクト教材を Hyperledger Fabric 上でやってみる](https://qiita.com/nekia/items/9a1acd2f811f31959839)

Hyperledger Fabric に EVM 載せようというプロジェクト [Hyperledger Burrow](https://www.hyperledger.org/projects/hyperledger-burrow) がありますがもう動くようになっていたのですね！Hyperledger 1.3 あたりからですかね？
[Hyperledger Fabricでイーサリアム・スマートコントラクトが実装可能に―BC基盤競争が加速](https://cointyo.jp/article/10005190)

ちなみに Hyperledger Burrow プロジェクトのリポジトリはこちら。

 - [https://github.com/hyperledger/fabric-chaincode-evm](https://github.com/hyperledger/fabric-chaincode-evm)
 
サンプルでの動かし方もチュートリアルがありました。

- [https://github.com/hyperledger/fabric-chaincode-evm/blob/master/examples/EVM_Smart_Contracts.md](https://github.com/hyperledger/fabric-chaincode-evm/blob/master/examples/EVM_Smart_Contracts.md)

最新バージョンではどうなんだろう？ということでやってみました。

# 環境構築
なにはともあれ、Ubuntu16:04+GoでのHyperledgerの開発環境を整えましょうか。
いつものようにDocker on LXDでまいります。

## 開発環境の構築

まず[こちらのでご紹介した手順](https://qiita.com/koinori/items/3063bc5091661e45508f)にて lxc の docker プロファイルを利用して Ubuntu16.04 のコンテナを作成します。

```console
$ lxc launch -p docker ubuntu:xenial hyperledger-go
$ lxc exec hyperledger-go bash
~#
```

ここに go-lang と docker をインストールしていきます。米国のarchive.ubuntu.com サーバーはレスポンス遅いので理研のftpサーバーをaptのリポジトリとします。

```
# sed -i.bak -e "s%http://[^ ]\+%http://ftp.riken.go.jp/Linux/ubuntu/%g" /etc/apt/sources.list
# apt update && apt upgrade -y
# cd /tmp && wget https://dl.google.com/go/go1.11.linux-amd64.tar.gz
# tar -xvf go1.11.linux-amd64.tar.gz
# mv go /usr/local
# rm go1.11.linux-amd64.tar.gz
# cd ~/
# mkdir go
# vi~/.bashrc
```

以下の環境変数を追加してGO言語の準備は完了です。

```
export GOROOT=/usr/local/go
export GOPATH=$HOME/go
export PATH=$GOPATH/bin:$GOROOT/bin:$PATH
```

引き続き以下の手順で Docker & docker-compose をインストールしていきます。

```
# apt-get install -y \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common
# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
# add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
# apt-get update
# apt-get install -y docker-ce docker-ce-cli containerd.io
# docker -v
# docker run hello-world
# curl -L https://github.com/docker/compose/releases/download/1.23.2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose
```

でDockerの動作確認とdocker-composeのインストール OK です。

## Hyperledger のダウンロード

Hyperledger Burrow プロジェクトのリポジトリからソースを cloneします。
また今回はHyperledgerそのものは EVM の動作確認用として使用するだけなのでFabric本体のダウンロードはせず、さくっとテストネットワークだけ立ち上げられるサンプルのプロジェクトのほうを使います。

```
# apt install -y make
# go get github.com/hyperledger/fabric-chaincode-evm
# cd $GOPATH/src/github.com/hyperledger/fabric-chaincode-evm
# make fab3
# cd $GOPATH/src/github.com/hyperledger
# git clone https://github.com/hyperledger/fabric-samples.git -b release-1.4
# cd fabric-samples
# ./scripts/bootstrap.sh
# export PATH=$PWD/bin:$PATH
# cd first-network
# ./byfn.sh up
```

で、テストネットが起動できれば OK です。

# スマートコントラクトの環境構築

## EVM チェインコードのインストール

Fabric上でスマートコントラクトを実行するモジュール"チェインコード"に対してEVMサポートを追加します。

いったん、テストネットワークを止めます。

```
# ./byfn.sh down
# vi docker-compose-cli.yaml
```

以下のボリュームを追加します。

```yaml:docker-compose-cli.yaml
       - ./../../fabric-chaincode-evm:/opt/gopath/src/github.com/hyperledger/fabric-chaincode-evm
```

テストネットを再度、起動します。

```
# ./byfn.sh up
```
以下のインストールスクリプトを作成します。

```
# vi scripts/installevm.sh
```

```shell: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
```

コンテナ内部で実行いたします。

```
# chmod a+x scripts/installevm.sh
# docker exec -it cli scripts/installevm.sh
```

これで Ethereumのスマートコントラクトを動かす準備が整いました。

## サンプルの実行

[本家サイトのチュートリアル](https://github.com/hyperledger/fabric-chaincode-evm/blob/master/examples/EVM_Smart_Contracts.md)にある通り、試しにバイナリのチェインコードを実行してみます。

```
# 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
```

以下のような OK メッセージが表示されれば OKです。

```
2019-03-20 08:29:08.382 UTC [chaincodeCmd] chaincodeInvokeOrQuery -> INFO 001 Chaincode invoke successful. result: status:200 payload:"5bf7e5ecd6e75cce05552c6cbd628d9616e4e09b"
```

# Ethereum スマートコントラクトの開発と動作確認

## Fab3 の準備

Hyperledger ネットワークを Ethereum のネットワークとして web3.js と対話させるためのサービスとして、Hyperledger Burrow プロジェクトでは Fab3 が提供されています。
以下のコマンドで Fab3 を起動します。

```
# cd $GOPATH/src/github.com/hyperledger/fabric-chaincode-evm
# vi fab3_env.sh
```

以下の環境設定ファイルを作成

```shell:fab3_env.sh
# Environment Variables for Fab3:
export FAB3_CONFIG=${GOPATH}/src/github.com/hyperledger/fabric-chaincode-evm/examples/first-network-sdk-config.yaml
export FAB3_USER=User1
export FAB3_ORG=Org1
export FAB3_CHANNEL=mychannel
export FAB3_CCID=evmcc
export FAB3_PORT=5000
```

source して fab3 を起動します。

```
# source fab3_env.sh
# ./bin/fab3
{"level":"info","ts":1553072051.4909546,"logger":"fab3","caller":"cmd/main.go:149","msg":"Starting Fab3","port":5000}
```

無事に起動出来たようなのでバックグラウンドに持っていくか別ターミナルを立ち上げましょう。

## Web3.js のインストール

Burrow プロジェクトのチュートリアルにもありますが、web3.jsのバージョンは"0.20.2"が宜しいようです。(最新版入れたらダメでした。)

```console
# npm install -g web3@0.20.2
# node
> 
```
でnodeのREPLモードに入ります。さっそくweb3.jsからHyperledgerネットワークにアクセスしてみましょう。

```javascript:node(REPL)
> Web3 = require('web3')
{ [Function: Web3]
  providers:
   { HttpProvider: [Function: HttpProvider],
     IpcProvider: [Function: IpcProvider] } }
> web3 = new Web3(new Web3.providers.HttpProvider('http://localhost:5000'))
Web3 {
  _requestManager:
   RequestManager {
     provider:
...

> web3.eth.accounts
[ '0x955b022fdbe8f163d53e8bfd9aaddd3c8377553f' ]
> web3.eth.defaultAccount = web3.eth.accounts[0]
'0x955b022fdbe8f163d53e8bfd9aaddd3c8377553f'
```
これで準備完了です。HyperledgerのネットワークはバッチリEthereumネットワークとして認識されていますね！

## Remix でバイナリの生成

ローカルにSolidity環境を構築するのはちょっと大変、というか今どきの推奨でもないので、ささっとオンラインの Remix を使ってしまいます。

- [https://remix.ethereum.org/](https://remix.ethereum.org/)

まずは[Solidityの本家マニュアル](https://solidity.readthedocs.io/en/latest/introduction-to-smart-contracts.html)に掲載されているSimpleStrageSampleを動かしてみましょう。

```solidity:SimpleStorage.sol
pragma solidity >=0.4.0 <0.7.0;

contract SimpleStorage {
    uint storedData;

    function set(uint x) public {
        storedData = x;
    }

    function get() public view returns (uint) {
        return storedData;
    }
}
```

このコードをコピーして Remixのエディタで貼り付け、コンパイラを "0.4.25" にセットします。
コンパイラのロードができたら `Ctrl+S`で保存するとコンパイラが走ります。
エラーがあった場合はコンパイラのバージョンを確認しましょう。0.5.x以上ではコンパイルできません。

エラーなどがなければ`ABI`と`Bytecode`のボタンをクリックしてそれぞれ適当なエディタに貼り付けておきます。

実は上記で最初にEVMの動作確認したバイナリがこのSimpleStorageのバイナリです。
peerコマンドではなく、web3経由でアクセスしてみたいと思います。

まずはABIから行きましょう。以下のように`SimpleStorageABI`を定義します。

```javascript:node(REPL)
> simpleStorageABI = [
...     {
...             "constant": false,
...             "inputs": [
...
```
つづいてバイナリコードを変数に入れておきます。

```javascript:node(REPL)
> simpleStorageBytecode = '608060405234801561001057600... '
```

これらを合わせてスマートコントラクトとし、ネットワーク上にデプロイします。

```javascript:node(REPL)
> SimpleStorage = web3.eth.contract(simpleStorageABI)
ContractFactory {
  eth:
   Eth {
     _requestManager: RequestManager { provider: [Object], polls: {}, timeout: null },
...

> deployedContract = SimpleStorage.new([], {data: simpleStorageBytecode})
Contract {
  _eth:
   Eth {
     _requestManager: RequestManager { provider: [Object], polls: {}, timeout: null },
```

こちらでデプロイされたスマートコントラクトにアクセスするインタフェースの`deployedContract`が取得できました。

また、今回は新規作成ですがデプロイ済みのスマートコントラクトには以下のメソッドでもアクセス可能です。

```javascript:node(REPL)
> myContract = SimpleStorage.at(web3.eth.getTransactionReceipt(deployedContract.transactionHash).contractAddress)
Contract {
  _eth:...
```
アドレスがわかっている場合はもっと簡単に

```javascript:node(REPL)
> myContract = SimpleStorage.at('0x127d9f8f199e363f2aa57dd125abb7b2d4f1c19a')
Contract {
  _eth:
   Eth {
     _requestManager: RequestManager { provider: [Object], polls: {}, timeout: null },
...
```
と直接、指定でもOKです。

それではこのスマートコントラクトの`set`と`get`メソッドを呼び出してみましょう。

```javascript:node(REPL)
> myContract.set(10)
'6e049d491457e1c2862a83183ced3be8ac841c0bc83e2ed079127a805a2fbdfb'
> myContract.get().toNumber()
10
```
Hyperledger FabricのブロックチェーンへのEthereumコントラクトからの書き込みと読み込み、ばっちりできていますね！

このように`ABI`と`Bytecode`から`SmartContract`を生成し、デプロイするか既存のアドレスを指定してインスタンスを取得し、メソッドを呼び出す、という一連の流れが確認できました。

## もう少しだけ複雑なスマートコントラクト

以下のサイトのチュートリアルのスマートコントラクトを試してみます。

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

上記サイトの後半にあるスマートコントラクトの完全版のコードをコピーしてRemixに貼り付け、ABIとBytecodeを取得します。例によってコンパイラは`0.40.2x`を指定して下さい。

準備が整ったらweb3.jsでの作業を開始します。

```javascript:node(REPL)
> gradeABI = [
	{
		"constant": true,
		"inputs": [
			{
				"name": "",
				"type": "bytes32"
			}
		],
...

> gradeBytecode = '6060604052341561000f57600080fd5b60405161069f38 ...`

> GradeContract = web3.eth.contract(gradeABI)

> myGrade = GradeContract.new(['John','James'], {data: gradeBytecode})

```
`GradeContract.new`で第一引数にコンストラクタに渡す値を指定しますが、ちゃんと動いていますね！
それではメソッドを叩いてみます。

```javascript:node(REPL)
> myGrade.giveGradeToStudent('John', 'A+')
'5ddbf87d76b011339d02128d1c6889c0fade7dad667af43f835e5841724c9e84'
> myGrade.getGradeForStudent("John")
'A+'
```

ばっちり良好です！

# まとめ
Solidity`0.40.25`でコンパイルしたバイナリはばっちり動作することがわかりました。
ローカルのHyperledgerでEVMのスマートコントラクト開発、アリじゃないかな？！ガス代かからないしコミット早いし。
というわけで引き続きSolidity`0.5.x`やもうちょっと複雑なスマートコントラクトなどにも挑戦してみたいと思います。

本日は以上です。