仮想通貨
ブロックチェーン
EOSIO

EOSIOで独自の仮想通貨トークンを発行・送金してみる

開発環境の構築とかはこちら参照:
https://qiita.com/YukiSekiguchi/items/623359857269aff74586
https://qiita.com/YukiSekiguchi/items/f84afbf35f4fa039859c

今回やること

https://developers.eos.io/eosio-home/docs/token-contract に沿ってEOSIOのプライベートネットワーク上で仮想通貨トークンを発行、送金してみる

準備

nodeosのコンテナが動いてないようなら動かす

$docker start 1574a37de3c6

まずはウォレットをopenする+unlockする。

$cleos wallet open -n default
$cleos wallet unlock -n default

トークン発行するスマートコントラクト用にアカウントを作成。開発用の鍵を使う。

$cleos create account eosio eosio.token EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV

トークン発行用のスマートコントラクトの実装は公式が提供してくれているので、これを持ってくる

$git clone https://github.com/EOSIO/eosio.contracts

そのままコンパイルするだけ。簡単。

$cd eosio.contracts
$eosio-cpp -o eosio.token.wasm src/eosio.token.cpp -I include/

トークンの作成

コンテナの中にeosio.token.wasmとabiを持っていったら

$ cleos set contract eosio.token <eosio.token.wasmがおいてあるコンテナ内のパス> --abi <eosio.token.abiのおいてあるコンテナ内のパス> -p eosio.token@active

とすると

Reading WASM from /home/eosio.contracts/eosio.token/eosio.token.wasm...
Publishing contract...
executed transaction: c12511f41efb3ec72e64a9ab8ce040f2c65c54a7d7d21ef847bb758b0b49e79c  9320 bytes  1490 us
#         eosio <= eosio::setcode               {"account":"eosio.token","vmtype":0,"vmversion":0,"code":"0061736d0100000001b0011d60037f7e7f0060047f...
#         eosio <= eosio::setabi                {"account":"eosio.token","abi":"0e656f73696f3a3a6162692f312e30010c6163636f756e745f6e616d65046e616d65...
2018-10-08T12:07:31.800 thread-0   main.cpp:481                  print_result   warning: transaction executed locally, but may not be confirmed by the network yet

と出てスマートコントラクトがネットワーク上にデプロイされる。
この状態で次のコマンドを打つとトークンが作成できる。

cleos push action eosio.token create '[ "eosio", "1000000000.0000 HOGE"]' -p eosio.token@active

1000000000.0000は発行上限が1000000000で、最小単位が0.0001になること表す。
またHOGEの部分は任意の大文字アルファベットを入れる。これがトークンの単位になる。普通は3文字にする(と思う)。
今回はこのままHOGEコインということで進める。

トークンの発行

発行(issue)と作成(create)は違う概念で、createはトークンの上限と最小単位と単位を規定し、実際のトークンはeosio.token自身が持つ。
トークンを通常のアカウント向けに渡すのが発行である(この辺の理解は怪しい)。

前回作ったalicebobにトークンを発行し、やりとりさせてみる。
まずalice100.0000 HOGE発行する。

$ cleos push action eosio.token issue '[ "alice", "100.0000 HOGE", "memo" ]' -p eosio@active
executed transaction: deafe9a28bbd20697c28f1e2a2b7c2997d589115c661d5010cbe25a909074460  128 bytes  6136 us
#   eosio.token <= eosio.token::issue           {"to":"alice","quantity":"100.0000 HOGE","memo":"memo"}
#   eosio.token <= eosio.token::transfer        {"from":"eosio","to":"alice","quantity":"100.0000 HOGE","memo":"memo"}
#         eosio <= eosio.token::transfer        {"from":"eosio","to":"alice","quantity":"100.0000 HOGE","memo":"memo"}
#         alice <= eosio.token::transfer        {"from":"eosio","to":"alice","quantity":"100.0000 HOGE","memo":"memo"}

memoの部分は文字通りメモで、取引について好きなメモを残せる。

なおHOGEコインの最小単位は0.0001にしたので、きっちり.0000まで書かないと以下のようにエラーになる。

$ cleos push action eosio.token issue '[ "alice", "100.0 HOGE", "memo" ]' -p eosio@active
Error 3050003: eosio_assert_message assertion failure

また、-dをつけるとdry runができる(見てもよくわからない)。

$ cleos_d push action eosio.token issue '[ "alice", "100.0000 HOGE", "memo" ]' -p eosio@active -d
{
  "expiration": "2018-10-08T12:41:28",
  "ref_block_num": 52702,
  "ref_block_prefix": 2512073667,
  "max_net_usage_words": 0,
  "max_cpu_usage_ms": 0,
  "delay_sec": 0,
  "context_free_actions": [],
  "actions": [{
      "account": "eosio.token",
      "name": "issue",
      "authorization": [{
          "actor": "eosio",
          "permission": "active"
        }
      ],
      "data": "0000000000855c3440420f000000000004484f4745000000046d656d6f"
    }
  ],
  "transaction_extensions": [],
  "signatures": [
    "SIG_K1_KexcdCmckuWp9bVBKeiLNDuLQA1L4Jenicy44TBTFxajF9dFrpAutrjRd4gvZdWrA8VA8ftrZ5qvEAF9FtDAYPHLpJPv2U"
  ],
  "context_free_data": []
}

アカウントが持つトークンは次のように確認できる

$ cleos get currency balance eosio.token alice HOGE
100.0000 HOGE

トークンの送金

これでalice100.0000HOGE持っているので、bobに送金してみる。
試しにmemoに日本語を入れたけど大丈夫だった(途中で切れてしまったが)。

$ cleos push action eosio.token transfer '[ "alice", "bob", "25.0000 HOGE", "アリスからボブに25.0000HOGE送金する" ]' -p alice@active
executed transaction: 8378a1d8577444796660e9ae5f417f71ad05bcfb54fa4f1e82ed4f65dacd1d5e  176 bytes  2931 us
#   eosio.token <= eosio.token::transfer        {"from":"alice","to":"bob","quantity":"25.0000 HOGE","memo":"アリスからボブに25.0000HOGE送�...
#         alice <= eosio.token::transfer        {"from":"alice","to":"bob","quantity":"25.0000 HOGE","memo":"アリスからボブに25.0000HOGE送�...
#           bob <= eosio.token::transfer        {"from":"alice","to":"bob","quantity":"25.0000 HOGE","memo":"アリスからボブに25.0000HOGE送�...
2018-10-08T12:45:34.574 thread-0   main.cpp:481                  print_result   warning: transaction executed locally, but may not be confirmed by the network yet

bobのトークン所有量を見てみると、

$cleos get currency balance eosio.token bob HOGE
25.0000 HOGE

となり、aliceのトークン所有量を見てみると、

$cleos get currency balance eosio.token alice HOGE
75.0000 HOGE

となっていて、aliceからbob25.0000 HOGE送金できたことがわかる。

ちなみに試しにbobが勝手にaliceからbobに送金しようとする(最後をbob@activeに変える)と何が起きるかというと

$ cleos push action eosio.token transfer '[ "alice", "bob", "25.0000 HOGE", "アリスからボブに勝手に25.0000HOGE送金する" ]' -p bob@active
Error 3090004: Missing required authority

となり、勝手なことはできないようになっている。

これはsrc/eosio.token.cppの中身を見てみると

void token::transfer( account_name from,
                      account_name to,
                      asset        quantity,
                      string       memo )
{
    eosio_assert( from != to, "cannot transfer to self" );
    require_auth( from );
    eosio_assert( is_account( to ), "to account does not exist");
    auto sym = quantity.symbol.name();
    stats statstable( _self, sym );
    const auto& st = statstable.get( sym );
︙
︙

となっており、前回もやったrequire_auth( from )が効いているからであることがわかる。