EOSIOでスマートコントラクトを実装してみる

EOSIOの概要と準備編はこちら：https://qiita.com/YukiSekiguchi/items/623359857269aff74586

# スマートコントラクトの実装
ようやくスマートコントラクトの実装に移る。
## 2.1 Hello World!
まずはhelloディレクトリを作成。

```
cd <ローカルの作業用ディレクトリの絶対パス>
mkdir hello
cd hello
```
続いて、hello.cppを実装する。

```
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>

using namespace eosio;

class hello : public contract {
  public:
      using contract::contract;

      [[eosio::action]]
      void hi( account_name user ) {
         print( "Hello, ", name{user} );
      }
};

EOSIO_ABI( hello, (hi) )
```
- 1-2行目：必要なライブラリをインクルード
- 4行目：`eosio`名前空間のものを修飾せずに使えるようにする
- 6行目：`contract`クラスを継承した`hello`クラスの宣言
- 7行目：アクセス修飾子`public`
- 8行目：`contract::contract`名前空間のものを修飾せずに使えるように
- 10行目：コンパイラがABIを作成するための属性
- 11行目：`account_name`型の変数`user`を引数とするメソッド`hi`の定義
- 12行目：アカウント名を`name{user}`で取得して`print`する
- 16行目：`hello`というコントラクトを実行するためのマクロ(?)

これをコンパイルする。

```
eosio-cpp -o hello.wasm hello.cpp --abigen
```
これで`wasm`形式のバイナリファイルと、`hello.abi`というjson形式のファイルが出来上がる(この辺はイーサリアム同様)。
`hello.abi`の中身はこんな感じ。

```
$ cat hello.abi
{
    "____comment": "This file was generated with eosio-abigen. DO NOT EDIT Mon Oct  1 00:06:48 2018",
    "version": "eosio::abi/1.0",
    "structs": [
        {
            "name": "hi",
            "base": "",
            "fields": [
                {
                    "name": "user",
                    "type": "name"
                }
            ]
        }
    ],
    "types": [],
    "actions": [
        {
            "name": "hi",
            "type": "hi",
            "ricardian_contract": ""
        }
    ],
    "tables": [],
    "ricardian_clauses": [],
    "abi_extensions": []
}
```

`--volume`オプションをつけて`docker`コマンドを実行したため、うまくいけばコンパイルしたファイル群はコンテナ側に入っているはず。ちゃんと入っていない場合は`docker cp`で運ぶ。
ブロックチェーン上で他のユーザーがアクセスできるようにするため、コントラクト用にアカウントを作成する。

```
cleos create account eosio hello YOUR_PUBLIC_KEY -p eosio@active
```
あとはこのアカウントでブロックチェーン上にコントラクトをbroadcastする

```
cleos set contract hello <コンテナの、コンパイルしたファイルが入っているパス> -p hello@active
```

これでブロックチェーン上の誰でもこのコントラクトを利用できるようになる。

```
Reading WASM from /home/hello/hello.wasm...
Publishing contract...
executed transaction: 6ca2cb39d7a23c95cb678b30ae9ffe9cc76abf8d0e32bca2f68a258fc0ced56f  1520 bytes  449 us
#         eosio <= eosio::setcode               {"account":"hello","vmtype":0,"vmversion":0,"code":"0061736d0100000001320a60027f7e0060027f7f00600001...
#         eosio <= eosio::setabi                {"account":"hello","abi":"0e656f73696f3a3a6162692f312e30000102686900010475736572046e616d650100000000...
warning: transaction executed locally, but may not be confirmed by the network yet    ]
```
早速このコントラクトを使ってみる。

```
cleos push action hello hi '["bob"]' -p bob@active
```
これは`hello`というアカウントの`hi`コントラクトを `'["bob"]'`というjson形式の引数で、`bob`というアカウントが`active`というパーミッションレベルで実行するものである。
これがうまくいくと、

```
executed transaction: 32913c03fe13777654f33f4a6e622dd4d1119a8122d509f01204d179e7e3a33d  104 bytes  315 us
#         hello <= hello::hi                    {"user":"bob"}
>> Hello, bob
warning: transaction executed locally, but may not be confirmed by the network yet    ]
```
となる。`docker logs --tail 100 eosio`でコンテナのログを表示すると、

```
2018-09-30T15:47:09.002 thread-0   producer_plugin.cpp:1419      produce_block        ] Produced block 0001729f34805d46... #94879 @ 2018-09-30T15:47:09.000 signed by eosio [trxs: 0, lib: 94878, confirmed: 0]
2018-09-30T15:47:09.099 thread-0   apply_context.cpp:28          print_debug          ]
[(hello,hi)->hello]: CONSOLE OUTPUT BEGIN =====================
Hello, bob
[(hello,hi)->hello]: CONSOLE OUTPUT END   =====================
```
となっていてコントラクトを実行したブロックが追加されたことがわかる。
コードからして当然だが、`alice`で`bob`に`hi`することができる。

```
$cleos push action hello hi '["bob"]' -p alice@active
executed transaction: f62c114ea4e2eabe0db9924b5146a2486a7fb9db0733e2845dcdb7cfa4f5a0f4  104 bytes  294 us
#         hello <= hello::hi                    {"user":"bob"}
>> Hello, bob
warning: transaction executed locally, but may not be confirmed by the network yet    ]
```

コードを変えることで、`bob`しか`bob`に`hi`できないようにする。
具体的には

```
void hi( account_name user ) {
   require_auth( user );
   print( "Hello, ", name{user} );
}
```

とする。`require_auth`は、コントラクトを実行したユーザーが引数である`user`であるかどうかを確かめるメソッドである。
これを再度コンパイル・broadcastし直す。

```
eosio-cpp -o hello.wasm hello.cpp --abigen
cleos set contract hello <コンテナの、コンパイルしたファイルが入っているパス> -p hello@active
```
ちなみに`wasm`が更新されていない状態で`cleos set contract...`し直すと以下のようなエラーが出る

```
$ cleos set contract hello /home/hello -p hello@owner
Reading WASM from /home/hello/hello.wasm...
Publishing contract...
Error 3160008: Contract is already running this version of code
```
今度は`alice`で`bob`に`hi`しようとすると

```
$cleos push action hello hi '["bob"]' -p alice@active
Error 3090004: Missing required authority
Ensure that you have the related authority inside your transaction!;
If you are currently using 'cleos push action' command, try to add the relevant authority using -p option.
```
というエラーが出る。一方で`alice`で`alice`に`hi`してみると

```
$ cleos push action hello hi '["alice"]' -p alice@active
executed transaction: 887d64024e1d9254560df4243ff127308f31fd86f8c6838728bf195d43167e07  104 bytes  481 us
#         hello <= hello::hi                    {"user":"alice"}
>> Hello, alice
warning: transaction executed locally, but may not be confirmed by the network yet    ]
```
となる。
超簡単なスマートコントラクトが実装できた。次回は独自の仮想通貨トークンを発行してやりとりしてみる。