Tendermintで簡単なアプリを動かすまで(残高を更新する)

#　概要

![printing-plate-1030849_1920.jpg](https://qiita-image-store.s3.amazonaws.com/0/206272/f8618ebf-a906-fec2-430c-68a6a4e5067c.jpeg)

前回に引き続きtendermintについて書きます。
今回は、Tendermintと実際にトランザクションに対してどのような処理をするかなどを決めるアプリケーションレイヤーをつなぐABCI(Application BlockChain Interface)をインストールし、簡単なアプリを動かしてみルことにします。
Bitcoinの場合は、UTXOというアプリケーションが実行され、Ethererumの場合は、EVMと呼ばれるアセンブラを実行できるようになっています。(Ethererumと同じ機能をTendermintで実装したものが、[Ethermint](https://github.com/tendermint/ethermint)です。)

前回の記事は、以下を確認してください。
[TendermintをLocal開発環境にインストールするまで](https://qiita.com/tring/items/7815f99f94acef268c6e)

# ABCIのインストール

前回に引き続き、Mac上でVitualboxを用いて作ったUbuntuにsshで接続し、はじめにABCIをインストールしていきます。[公式のドキュメント](https://tendermint.readthedocs.io/en/master/getting-started.html)には、go getを用いてインストールするように書いてあるが、この方法ではうまくいかなかったので、git cloneを用います。 git をまだインストールしていなければ、はじめに以下を実行します。

```
$ sudo apt-get update
$ sudo apt-get install git
$ git --version
  git version x.x.x
```

インストールされている場合は、前回作った、tendermintディレクトリーへ移動します。

```
$ cd 
$ cd usr/local/go/src/github.com/tendermint
```

ABCIをcloneするためのディレクトリーを作し、cloneします。

```
$ mkdir abci
$ cd abci
$ git clone https://github.com/tendermint/abci 
```
これで、ABCIをインストールする準備が整った。ちなみに、abciディレクトリーには、次のようなファイルが配置されてあるはずです。

```
$ ls

CHANGELOG.md        example     Makefile   specification.md   version
client              Gopkg.lock  README.md  specification.rst
cmd                 Gopkg.toml  scripts    tests
Dockerfile.develop  LICENSE     server     types
```

最後にABCIをインストールする。Goのファイルがある、abci/cmd/abci-cli へ移動し、インストール。

```
$ cd cmd 
$ cd abci-cli
$ go install 
```

正しくインストールされている場合は、versionが表示されます。

```
$ abci-cli version
```

# Tendermintを動かそう!!

これでやっとtendermintを動かす準備が整いました。

##KV-store

まず、はじめにabci-cliでKVstoreを起動します。

>KVstoreは、key-valueをMerkle Treeでメモリーに保存するアプリケーション
>https://github.com/tendermint/abci/blob/master/cmd/abci-cli/abci-cli.go で実装されている。



```
$ abci-cli kvstore
```

すると、

```
E[06-08|05:22:38.311] Connection was closed by client              module=abci-server 
E[06-08|05:22:38.311] Connection was closed by client              module=abci-server 
E[06-08|05:22:38.311] Connection was closed by client              module=abci-server
 
```

と表示されるはず。これは、Tendermintの方がActiveになっていないからです。

そこで次にブロックチェーン部分をTendermintで動かすために、ターミナルを別でもう一つ開き、こちらでnodeを起動します。

```
$ tendermint init
$ tendermint node
```

すると、ブロック生成が始まります。例えば、ここでは2601番目に生成されたBlockについての一例を載せる。各時刻でpropose,commit,prevote,voteなどが行われているのがわかります。

```
sus dur=978.453767ms height=2601 round=0 step=RoundStepNewHeight
I[06-09|14:51:21.761] enterNewRound(2601/0). Current: 2601/0/RoundStepNewHeight module=consensus height=2601 round=0


I[06-09|14:51:21.763] enterPropose(2601/0). Current: 2601/0/RoundStepNewRound module=consensus height=2601 round=0


I[06-09|14:51:21.764] enterPropose: Our turn to propose            module=consensus height=2601 round=0 proposer=5B0BA5F9032515CEE082E8A9391963CB3F200A61 privValidator="PrivValidator{5B0BA5F9032515CEE082E8A9391963CB3F200A61 LH:2600, LR:0, LS:3}"


I[06-09|14:51:21.777] Signed proposal                              module=consensus height=2601 round=0 proposal="Proposal{2601/0 1:AA8E53C09AED (-1,:0:000000000000) /BC9E69644173.../ @ 2018-06-09T14:51:21.765Z}"


I[06-09|14:51:21.782] Received proposal                            module=consensus proposal="Proposal{2601/0 1:AA8E53C09AED (-1,:0:000000000000) /BC9E69644173.../ @ 2018-06-09T14:51:21.765Z}"


I[06-09|14:51:21.785] Received complete proposal block             module=consensus height=2601 hash=AB81757ACFF46EBE53459A93B30536278DFE6FD7


I[06-09|14:51:21.786] enterPrevote(2601/0). Current: 2601/0/RoundStepPropose module=consensus 


I[06-09|14:51:21.787] enterPrevote: ProposalBlock is valid         module=consensus height=2601 round=0


I[06-09|14:51:21.791] Signed and pushed vote                       module=consensus height=2601 round=0 vote="Vote{0:5B0BA5F90325 2601/00/1(Prevote) AB81757ACFF4 /A9DAF094A48C.../ @ 2018-06-09T14:51:21.788Z}" err=null


I[06-09|14:51:21.793] Added to prevote                             module=consensus vote="Vote{0:5B0BA5F90325 2601/00/1(Prevote) AB81757ACFF4 /A9DAF094A48C.../ @ 2018-06-09T14:51:21.788Z}" prevotes="VoteSet{H:2601 R:0 T:1 +2/3:AB81757ACFF46EBE5345B30536278DFE6FD7:1:AA8E53C09AED(1) BA{1:x} map[]}"


I[06-09|14:51:21.795] enterPrecommit(2601/0). Current: 2601/0/RoundStepPrevote module=consensus height=2601 round=0


I[06-09|14:51:21.796] enterPrecommit: +2/3 prevoted proposal block. Locking module=consensus height=2601 round=0 hash=AB81757ACFF46EBE53459A93B30536278DFE6FD7


I[06-09|14:51:21.799] Signed and pushed vote                       module=consensus height=2601 round=0 vote="Vote{0:5B0BA5F90325 2601/00/2(Precommit) AB81757ACFF4 /1622EEB995CA.../ @ 2018-06-09T14:51:21.797Z}" err=null


I[06-09|14:51:21.803] Added to precommit                           module=consensus vote="Vote{0:5B0BA5F90325 2601/00/2(Precommit) AB81757ACFF4 /1622EEB995CA.../ @ 2018-06-09T14:51:21.797Z}" precommits="VoteSet{H:2601 R:0 T:2 +2/3:AB81757ACFF46EBE534A93B30536278DFE6FD7:1:AA8E53C09AED(1) BA{1:x} map[]}"


I[06-09|14:51:21.804] enterCommit(2601/0). Current: 2601/0/RoundStepPrecommit module=consensus height=2601 commitRound=0


I[06-09|14:51:21.805] Commit is for locked block. Set ProposalBlock=LockedBlock module=consensus height=2601 commitRound=0 blockHash=AB81757ACFF46EBE53459A930536278DFE6FD7


I[06-09|14:51:21.807] Finalizing commit of block with 0 txs        module=consensus height=2601 hash=AB81757ACFF46EB3459A93B30536278DFE6FD7 root=0400000000000000


I[06-09|14:51:21.808] Block{
  Header{
    ChainID:        test-chain-wTDnfM
    Height:         2601
    Time:           2018-06-09 14:51:21.765544508 +0000 UTC
    NumTxs:         0
    TotalTxs:       0
    LastBlockID:    D33328921E1FD7CBD1DC594A6DE44CE08AFB6:1:A39CFAB37A55
    LastCommit:     F88AC710A4D17FF1B7D2FD7ABA5B285E0DE412F
    Data:           
    Validators:     33B01D34ED11911057391291BFCF8458F4E0
    App:            0400000000000000
    Consensus:       F66EF1DF8B6DAC7A1ECCE40CC84E54A1CEBC6A5
    Results:        
    Evidence:       
  }#AB81757ACFF46EBE459A930536278DFE6FD7
  Data{
    
  }#
  Data{
    
  }#
  Commit{
    BlockID:    D33328921ED7CBD1DC594A6DE44CFC0E08AFB6:1:A39CFAB37A55
    Precommits: Vote{0:5B0BA5F90325 2600/00/2(Precommit) D33328921E1F /0661C7433900.../ @ 2018-06-09T14:51:20.751Z}
  }#F88AC710A4D117FF1B2FD7ABA5B285E0DE412F
}#AB81757ACFF46EBE53459A93B30536278DFE6FD7 module=consensus 
I[06-09|14:51:21.821] Executed block                               module=state height=2601 validTxs=0 invalidTxs=0
I[06-09|14:51:21.824] Committed state                              module=state height=2601 txs=0 appHash=0400000000000000
I[06-09|14:51:21.824] Recheck txs                                  module=mempool numtxs=0 height=2601
I[06-09|14:51:21.830] Indexed block                                module=txindex height=2601

```
このコードを見ると、 deliver_tx, check_tx, and commitなどが確認できます。

KV-storeは以下のように実装されています。

```
func cmdKVStore(cmd *cobra.Command, args []string) error {
        logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))

        // Create the application - in memory or persisted to disk
        var app types.Application
        if flagPersist == "" {
                app = kvstore.NewKVStoreApplication()
        } else {
                app = kvstore.NewPersistentKVStoreApplication(flagPersist)
                app.(*kvstore.PersistentKVStoreApplication).SetLogger(logger.With("module", "kvstore"))
        }

        // Start the listener
        srv, err := server.NewServer(flagAddrD, flagAbci, app)
        if err != nil {
                return err
        }
        srv.SetLogger(logger.With("module", "abci-server"))
        if err := srv.Start(); err != nil {
                return err
        }

        // Wait forever
        cmn.TrapSignal(func() {
                // Cleanup
                srv.Stop()
        })
        return nil
}
```

これを用いれば、keyとvalueをBlockchainを用いて保管できます。

ABCIアプリケーションは、次の二つの要素を提供できるように設計しなけらばなりません。

1. サーバーソケット
2. ABCIのメッセージを扱える

abci-clientに関するtoolを実行するとき、まずはじめにアプリケーションソケットサーバーのコネクションを開始します。abci-clientは、命令されたabciのメッセージを送信し、responseを待つように設計されています。

![238EC3E5-2148-44A0-BB17-6C4B4345518B.jpg](https://qiita-image-store.s3.amazonaws.com/0/206272/b7f90df3-13e1-3337-07da-7aab2f9a517d.jpeg)


```abci-cli info```コマンドでも、新しいコネクションが開始されます。
info()では、トランザクションの回数を教えてくれます。

Client側で

```
$ abci-cli info
```

を実行します。すると、サーバー側では、次のような更新がlogとして確認できます。


```
I[06-10|03:57:46.389] Accepted a new connection                    module=abci-server 
I[06-10|03:57:46.389] Waiting for new connection...                module=abci-server 
E[06-10|03:57:46.394] Connection was closed by client              module=abci-server 

```
はじめに、new connectionを検出して、操作が終了すると、接続がcloseするのがわかります。

このように、全てのコマンドは、そのコマンドごとに新しいコネクションが作られ実行されコネクションが閉じるようになっています。もし、一度のコネクションで複数の操作を行いたい場合は、 ```abci-cli console```  や```abci-cli batch```を用いることで実行できます。

abci-cliコンソールを実行すると、アプリケーションにABCIメッセージを送るためのインタラクティブコンソールが表示されます。


## 0xabというアドレスの残高を100に設定してみます。

それでは試してみる。tendermint info などでもコマンドを実行できるが、deliver_txを実行すると、以下のようにクォテーションマークで囲っているにも関わらずエラーがでます。

```
abci-cli deliver_tx "abc"
I[06-10|01:34:21.307] Starting socketClient                        module=abci-client impl=socketClient
Error: Invalid string arg: "abc". Must be quoted or a "0x"-prefixed hex string
Usage:
  abci-cli deliver_tx [flags]

Flags:
  -h, --help   help for deliver_tx

Global Flags:
      --abci string        either socket or grpc (default "socket")
      --address string     address of application socket (default "tcp://0.0.0.0:46658")
      --log_level string   set the logger level (default "debug")
  -v, --verbose            print the command and results as if it were a console session

Invalid string arg: "abc". Must be quoted or a "0x"-prefixed hex stringroot@vagr
```
そのため、ABCIのconsoleでこれらを実行します。

```
$ abci-cli console

>deliver_tx "0xab=100"
-> code: OK

//コミットする。
> commit
-> code: OK
-> data.hex: 0x0400000000000000

//きちんと保存されているか確認します。

> query "0xab"
-> code: OK
-> log: exists
-> height: 0
-> value: 100
-> value.hex: 313030

```
これでうまく言っていることがわかります。
Note that if we do deliver_tx "abc" it will store (abc, abc), but if we do deliver_tx "abc=efg" it will store (abc, efg).

残高を200に修正したかった場合は、上記と同様の操作を行えば良いです。

```
>deliver_tx "0xab=200"
-> code: OK

> query "0xab"
-> code: OK
-> log: exists
-> height: 0
-> value: 200
-> value.hex: 31a0b0

```
### curlによるトランザクションの確認
```
curl -s localhost:46657/status
```
curlでtendermintで生成しているBlockの情報を手に入れられます。

```
{
  "jsonrpc": "2.0",
  "id": "",
  "result": {
    "node_info": {
      "id": "4250d8d0178118af34a66f4edaebb763cc4291",
      "listen_addr": "10.0.2.15:46656",
      "network": "test-chain-wTDnfM",
      "version": "0.20.0",
      "channels": "4020212223303800",
      "moniker": "vagrant-ubuntu-trusty-64",
      "other": [
        "amino_version=0.9.7",
        "p2p_version=0.5.0",
        "consensus_version=v1/0.2.2",
        "rpc_version=0.7.0/3",
        "tx_index=on",
        "rpc_addr=tcp://0.0.0.0:46657"
      ]
    },
    "sync_info": {
      "latest_block_hash": "8BB766A48020B74344D9BB7ABB9920782C4FB9A7",
      "latest_app_hash": "1000000000000000",
      "latest_block_height": 6180,
      "latest_block_time": "2018-06-10T04:26:53.799625697Z",
      "syncing": false
    },
    "validator_info": {
      "address": "5B0BA5F92515CEE082E8A9391963CB3F200A61",
      "pub_key": {
        "type": "AC26791624DE60",
        "value": "a57Dfsem2RUstwRilspsdF00+diohYHU6bPM9gEOE="
      },
      "voting_power": 10
    }
  }

```

次に先ほど同様に、key-valueを保存してみます。


```
$ curl -s 'localhost:46657/broadcast_tx_commit?tx="name=satoshi"'

{
  "jsonrpc": "2.0",
  "id": "",
  "result": {
    "check_tx": {
      "fee": {}
    },
    "deliver_tx": {
      "tags": [
        {
            
          "key": "YXBwLmNyZWF0b3I=",
　　　　　　　　　　　　　　　　　　　　　　　　//decodeするとapp.creator
          "value": "amFl"
            //decodeするとjae(tendermintのCEOの名前が!!!)
        },
        {
            //decodeするとapp.keyと書いてある。
          "key": "YXBwLmtleQ==",
            //decodeするとnameと書いてある。
          "value": "bmFtZQ=="
        }
      ],
      "fee": {}
    },
    "hash": "1B927707DADF01044D3D6E5D95257F53F9537BEE",
    "height": 6822
  }
```
ちなみに、Applicationの作成者にTendermintのCEOであるJaeさんの名前が書いてあるところが面白いところ!

このcommitでブロックに格納された情報は、次の様にしてみることができます。

```
$ curl -s 'localhost:46657/abci_query?data="name"'
{
  "jsonrpc": "2.0",
  "id": "",
  "result": {
    "response": {
      "log": "exists",
      "index": "-1",
      "key": "bmFtZQ==",
        //decodeするとsatoshiと書いてある。
      "value": "c2F0b3NoaQ=="
    }
  }

```


## トラブルシューティング

tendermintを動かし、その後一旦止めて、また動かすと、次のようにExecuted block とApplying blockを繰り返します。

```
$ tendermint init
$ tendermint node 

I[06-10|01:20:39.617] Executed block                               module=consensus height=3911 validTxs=0 invalidTxs=0
I[06-10|01:20:39.617] Applying block                               module=consensus height=3912
I[06-10|01:20:39.618] Executed block                               module=consensus height=3912 validTxs=0 invalidTxs=0
I[06-10|01:20:39.618] Applying block                               module=consensus height=3913
I[06-10|01:20:39.620] Executed block                               module=consensus height=3913 validTxs=0 invalidTxs=0
I[06-10|01:20:39.636] Applying block                               module=consensus height=3914

```

これは、有効なバリデータを設定できていないからでです。
そこで、一度tendermintをリセットする必要があります。

```
tendermint unsafe_reset_all
```
をすれば、以前同様に、ブロック生成がきちんとされるようになります。



# Bench-Mark
上記のように構成したUbuntuでtendermintのbenchmarkを行った。結果は、以下に示します。
簡単なトランザクションであるkvstoreでは、

```
$ tendermint node --proxy_app=kvstore
```
別ウィンドで、

```
tm-bench -T 10 -r 1000 localhost:46657
```
### 結果

|Stats|Avg|StdDev|Max|
|:------------|-------------:|------------:|:------------:|
|Txs/sec |735|511|1717|    
Blocks/sec |    0.900   |  0.300      |1 

TPSはいい感じ!!

# reference

decodingには、以下のサイトを用いました。
http://www.convertstring.com/ja/EncodeDecode/Base64Decode