EOSIOの開発環境構築について

by 仲尾 和祥

はじめに

ブロックチェーンスタジオでは、ブロックチェーンを用いたサービスの研究開発のため、ブロックチェーン基盤に関する調査を行なっています。今回は、パブリックブロックチェーン基盤の1つであるEOSの開発環境の構築方法について紹介します。

ちなみに、EOSは他のパブリックブロックチェーン基盤の仕様と大きく異なる部分があるので、補足説明をNoteという形で記載しています。

開発環境の構成

EOSのコントラクト開発環境は下記の図の構成になります。各コンポーネントの役割を下記に記載します。

  • nodeos:
    EOSのブロックチェーンネットワークを構成するノードの1つ。クライアント側のブロックの生成等のAPIのエンドポイント。
  • cleos:
    nodeosやkeosdを操作するためのコマンドラインインターフェイス。
  • keosd:
    EOSで扱うウォレットの管理。
  • wallet:
    EOSのアカウントに紐づけるための暗号鍵の保存。

EOS Develop Environment

Note: keosdとnodeosは、REST APIでも操作可能である。(参考:keosdのAPInodeosのAPI)

構築手順

実行環境の構築

Dockerを用いた構築手順と、Linux環境上へ直接構築する手順の2つを記載しておきます。

Note: CPUが2コア以上の環境でないと、コントラクトのデプロイ時にエラーが発生するので注意が必要(参考)

Dockerを用いた構築

EOSIOは、開発者用にDockerイメージが公開されているため、下記のようなDocker Compose用の定義を作成することで開発環境を用意することができます。ちなみに、共有ディレクトリの使用目的は下記の通り。

  • contracts: コントラクトのソースコード
  • build: コントラクトのビルド成果物
  • work: その他の作業用
version: '3.7'

services:
  nodeos:
    image: eosio/eos-dev:v1.5.2
    volumes:
      - ./contracts:/contracts
      - ./build:/build
      - ./work:/work
    ports:
      - 8888:8888
    command: /bin/bash -c 'nodeos -e -p eosio --plugin eosio::producer_plugin --plugin eosio::history_plugin --plugin eosio::chain_api_plugin --plugin eosio::history_api_plugin --plugin eosio::http_plugin -d /mnt/dev/data --config-dir /mnt/dev/config --http-server-address=0.0.0.0:8888 --access-control-allow-origin=* --contracts-console --http-validate-host=false'
  keosd:
    image: eosio/eos-dev:v1.5.2
    ports:
      - 9876:9876
    command: /bin/bash -c "keosd --plugin eosio::http_plugin --http-server-address=0.0.0.0:9876 --access-control-allow-origin=* --http-validate-host=false"
    links:
      - nodeos

Ubuntu上に構築

keosd用のsystemd設定ファイル

[Unit]
Description=boot keosd
After=network.service

[Service]
ExecStart=/usr/opt/eosio/1.5.0/bin/keosd
KillMode=process
PrivateTmp=true

[Install]
WantedBy=multi-user.target

nodeos用のsystemd設定ファイル

[Unit]
Description=boot nodeos
After=network.service keosd.service

[Service]
ExecStart=/usr/opt/eosio/1.5.0/bin/nodeos -e -p eosio --plugin eosio::producer_plugin --plugin eosio::chain_api_plugin --plugin eosio::http_plugin --plugin eosio::history_plugin --plugin eosio::history_api_plugin --access-control-allow-origin='*' --contracts-console --http-validate-host=false --verbose-http-errors --filter-on='*'
KillMode=process
PrivateTmp=true

[Install]
WantedBy=multi-user.target

環境構築用のスクリプト(例)

#!/bin/bash

set -x

apt update

which keosd > /dev/null
if [ $? -ne 0 ]; then
    wget https://github.com/eosio/eos/releases/download/v1.5.0/eosio_1.5.0-1-ubuntu-18.04_amd64.deb
    apt install -y ./eosio_1.5.0-1-ubuntu-18.04_amd64.deb
    rm ./eosio_1.5.0-1-ubuntu-18.04_amd64.deb
fi

which eosio-cpp > /dev/null
if [ $? -ne 0 ]; then
    wget https://github.com/EOSIO/eosio.cdt/releases/download/v1.4.1/eosio.cdt-1.4.1.x86_64.deb
    apt install -y ./eosio.cdt-1.4.1.x86_64.deb
    rm ./eosio.cdt-1.4.1.x86_64.deb
fi

SYSTEM_DIR='/usr/lib/systemd/system'

if [ ! -d ${SYSTEM_DIR} ]; then
    mkdir -p ${SYSTEM_DIR}
fi

if [ ! -f ${SYSTEM_DIR}/keosd.service ]; then
    mv /vagrant/conf/keosd.service ${SYSTEM_DIR}/keosd.service
    systemctl enable keosd.service
    systemctl start keosd.service
fi

if [ ! -f ${SYSTEM_DIR}/nodeos.service ]; then
    mv /vagrant/conf/nodeos.service ${SYSTEM_DIR}/nodeos.service
    systemctl enable nodeos.service
    systemctl start nodeos.service
fi

開発用アカウントの作成

暗号鍵(秘密鍵/公開鍵)の作成

Note: EOSIOのアカウントに紐づけるための暗号鍵(秘密鍵/公開鍵)を作成します。
EOSの秘密鍵と公開鍵は、EDCSA(楕円曲線暗号)鍵で生成されており、walletに以下の形式で保存されています。
* 秘密鍵:Bitcoinと同じWIF(Wallet Import Format)形式
* 公開鍵:Base58でエンコードされた文字列の先頭に「EOS」を付けた形式

“default” walletの作成

keosdに”default”という名前のwalletを作成します。

$ cleos wallet create --to-console
Creating wallet: default
Save password to use in the future to unlock this wallet.
Without password imported keys will not be retrievable.
"PW5JLKqjTUZ1rD27G31qPioXSDf7h5GhYp9ZuHvtDuVGEmERYYzyZ" # MASTER PASSWORD

上記に表示されている”MASTER PASSWORD”は、このwalletをunlockするために利用するため、必ず保存しておいてください。

Note: 作成したwalletの状態を確認するコマンド

$ cleos wallet list
Wallets:
[
  "default *"
]

“*”が付いているwalletはunlockされています。
一定時間経つとlockされてしまうため、unlockの際は以下のコマンドを実行する必要があります。

$ cleos wallet unlock
password: # MASTER PASSWORD

“default” walletの鍵ペアの作成

“default” walletに秘密鍵を確認せずに作成するため、以下のコマンドを実行します。

$ cleos wallet create_key
Created new private key with a public key of: "EOS8aBsRELYzsAoZi9yngABbdUiDbCawCgV5GaprjS78a9CiTVhqp"

Note①: “default” walletにインポートされている秘密鍵と公開鍵を確認するコマンド

$ cleos wallet private_keys
password: # MASTER PASSWORD
[
  [
    "EOS5GifRStJwiXmQDXyFquRrMMVEBLCCcASoYsD3QENGePKYein2T", # public key
    "5KjDY3uUJUEwH92mVa2YuZtnqeXXXXXXXXXXXXXXXXXXXXXXXXX" # private key
  ]
]

Note②: 秘密鍵と公開鍵を作成して登録する場合

$ cleos create key --to-console
Private key: 5KbmHJGdyb1WJCMCzeYXLY7ZwUSUAya1B2aYMrsDhE39tzGYQMD
Public key: EOS5xPkLMUhNBnzmWfu5X5Z7wrqkcNcanWZq3oGtZjwGEeaggtKjD
$ cleos wallet import --private-key 5KbmHJGdyb1WJCMCzeYXLY7ZwUSUAya1B2aYMrsDhE39tzGYQMD
imported private key for: EOS5xPkLMUhNBnzmWfu5X5Z7wrqkcNcanWZq3oGtZjwGEeaggtKjD

Note③: 登録した秘密鍵を削除するコマンド

$ cleos remove_key EOS5xPkLMUhNBnzmWfu5X5Z7wrqkcNcanWZq3oGtZjwGEeaggtKjD # public keyを指定
password: # MASTER PASSWORD

アカウントの作成

“default” walletに”eosio”アカウントの秘密鍵をインポート

EOSでは、”eosio”アカウントをシステム側が初期ユーザとして用意しています。ただし、このアカウントの秘密鍵は公開されているため、プロダクション環境のユーザとして利用するとOwner Permissionの秘密鍵が公開されることになるため、利用しないでください。

$ cleos wallet import --private-key 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
imported private key for: EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV

“alice” アカウント作成

$ cleos create account eosio alice EOS8aBsRELYzsAoZi9yngABbdUiDbCawCgV5GaprjS78a9CiTVhqp
executed transaction: 820340065bcae23cbd1048699ccb23e8c60d0e171c23d094a685c5e6b043ab9a  200 bytes  312 us

#         eosio <= eosio::newaccount            {"creator":"eosio","name":"alice","owner":{"threshold":1,"keys":[{"key":"EOS8aBsRELYzsAoZi9yngABbdUi...
warning: transaction executed locally, but may not be confirmed by the network yet         ] 

Note①: 作成したアカウントのPemissionと確保リソースに関する情報の確認コマンド

$ cleos get account alice
created: 2019-03-19T01:39:51.500
permissions: 
     owner     1:    1 EOS8aBsRELYzsAoZi9yngABbdUiDbCawCgV5GaprjS78a9CiTVhqp
        active     1:    1 EOS8aBsRELYzsAoZi9yngABbdUiDbCawCgV5GaprjS78a9CiTVhqp
memory: 
     quota:       unlimited  used:      2.66 KiB  

net bandwidth: 
     used:               unlimited
     available:          unlimited
     limit:              unlimited

cpu bandwidth:
     used:               unlimited
     available:          unlimited
     limit:              unlimited

Note②: mainnetでアカウントを作成する場合、permissionsに記載されているownerとactiveは、別々の鍵を使用することを推奨されています。
これは、ownerの鍵は、アカウントに関する全ての権限を保持しており、activeの鍵を紛失しても再設定可能なため、アカウントの凍結をしない等のリスク低減が見込めるためです。

Note③: EOSでは、アカウント作成のためにEOSにトランザクションを発火しています。トランザクションの確認コマンドは下記になります。

$ cleos get transaction 820340065bcae23cbd1048699ccb23e8c60d0e171c23d094a685c5e6b043ab9a
{
  "id": "820340065bcae23cbd1048699ccb23e8c60d0e171c23d094a685c5e6b043ab9a",
  "trx": {
    "receipt": {
      "status": "executed",
      "cpu_usage_us": 312,
      "net_usage_words": 25,
      "trx": [
        1,{
          "signatures": [
            "SIG_K1_Kaz1SWLz9Qa2scc7kgH9ZSqXfPdFh2zunGgw2rNCsZ7yvhimrzKzKzmDKn8rtkEcBy3gY3dGxbj3BLTevyJd> Nkww1gJp8R"
          ],
          "compression": "none",
          "packed_context_free_data": "",
          "packed_trx": "8548905cec05c99cda4200000000010000000000ea305500409e9a2264b89a010000000000ea305500000000a8ed3232660000000000ea30550000000000855c3401000000010003e53a8d8049bf4e56a6cd87dc65150a30eff89e190b2092e14a6bc25d757216c50100000001000000010003e53a8d8049bf4e56a6cd87dc65150a30eff89e190b2092e14a6bc25d757216c50100000000"
        }
      ]
    },
    "trx": {
      "expiration": "2019-03-19T01:40:21",
      "ref_block_num": 1516,
      "ref_block_prefix": 1121623241,
      "max_net_usage_words": 0,
      "max_cpu_usage_ms": 0,
      "delay_sec": 0,
      "context_free_actions": [],
      "actions": [{
          "account": "eosio",
          "name": "newaccount",
          "authorization": [{
              "actor": "eosio",
              "permission": "active"
            }
          ],
          "data": {
            "creator": "eosio",
            "name": "alice",
            "owner": {
              "threshold": 1,
              "keys": [{
                  "key": "EOS8aBsRELYzsAoZi9yngABbdUiDbCawCgV5GaprjS78a9CiTVhqp",
                  "weight": 1
                }
              ],
              "accounts": [],
              "waits": []
            },
            "active": {
              "threshold": 1,
              "keys": [{
                  "key": "EOS8aBsRELYzsAoZi9yngABbdUiDbCawCgV5GaprjS78a9CiTVhqp",
                  "weight": 1
                }
              ],
              "accounts": [],
              "waits": []
            }
          },
          "hex_data": "0000000000ea30550000000000855c3401000000010003e53a8d8049bf4e56a6cd87dc65150a30eff89e190b2092e14a6bc25d757216c50100000001000000010003e53a8d8049bf4e56a6cd87dc65150a30eff89e190b2092e14a6bc25d757216c501000000"
        }
      ],
      "transaction_extensions": [],
      "signatures": [
        "SIG_K1_Kaz1SWLz9Qa2scc7kgH9ZSqXfPdFh2zunGgw2rNCsZ7yvhimrzKzKzmDKn8rtkEcBy3gY3dGxbj3BLTevyJdNkww1gJp8R"
      ],
      "context_free_data": []
    }
  },
  "block_time": "2019-03-19T01:39:51.500",
  "block_num": 1518,
  "last_irreversible_block": 1716,
  "traces": [{
      "receipt": {
        "receiver": "eosio",
        "act_digest": "736653034d0eda46f1576d66da589f090b1b996af6442ca08d6074c35b5999dc",
        "global_sequence": 1518,
        "recv_sequence": 1518,
        "auth_sequence": [[
            "eosio",
            1518
          ]
        ],
        "code_sequence": 0,
        "abi_sequence": 0
      },
      "act": {
        "account": "eosio",
        "name": "newaccount",
        "authorization": [{
            "actor": "eosio",
            "permission": "active"
          }
        ],
        "data": {
          "creator": "eosio",
          "name": "alice",
          "owner": {
            "threshold": 1,
            "keys": [{
                "key": "EOS8aBsRELYzsAoZi9yngABbdUiDbCawCgV5GaprjS78a9CiTVhqp",
                "weight": 1
              }
            ],
            "accounts": [],
            "waits": []
          },
          "active": {
            "threshold": 1,
            "keys": [{
                "key": "EOS8aBsRELYzsAoZi9yngABbdUiDbCawCgV5GaprjS78a9CiTVhqp",
                "weight": 1
              }
            ],
            "accounts": [],
            "waits": []
          }
        },
        "hex_data": "0000000000ea30550000000000855c3401000000010003e53a8d8049bf4e56a6cd87dc65150a30eff89e190b2092e14a6bc25d757216c50100000001000000010003e53a8d8049bf4e56a6cd87dc65150a30eff89e190b2092e14a6bc25d757216c501000000"
      },
      "context_free": false,
      "elapsed": 50,
      "console": "",
      "trx_id": "820340065bcae23cbd1048699ccb23e8c60d0e171c23d094a685c5e6b043ab9a",
      "block_num": 1518,
      "block_time": "2019-03-19T01:39:51.500",
      "producer_block_id": null,
      "account_ram_deltas": [{
          "account": "alice",
          "delta": 2724
        }
      ],
      "except": null,
      "inline_traces": []
    }
  ]
}

コントラクトの作成(例)

EOSのコントラクトは、C/C++でソースコードを作成することを公式で推奨されています。なお、作成されたコントラクトは、WASM(Web Assembly)ベースのバイナリファイル、およびインターフェイスを定義したABIファイルにコンパイルされ、EOSへデプロイされます。

Note: WASM(Web Assembly)とは、ブラウザ上でリッチコンテンツへのニーズに対応するために開発された、ブラウザ上で実行されるバイナリ言語です。
JavaScriptのようなスクリプト言語は、構文解析や動的型付け言語の解析等を実行時に挟む必要があり、実行時間が遅くなるという課題を解決するため、開発されました。(参考)

作業用ディレクトリの作成

$ mkdir -p contracts/hello

サンプルコードの作成

トランザクションの引数に指定したアカウント名が、トランザクションを実行したアカウントと同名の場合、Hello, <account>というメッセージを表示するコントラクトを作成します。

#include <eosiolib/eosio.hpp>

using namespace eosio;

class [[eosio::contract("hello")]] hello : public contract {
  public:
      using contract::contract;

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

EOSIO_DISPATCH( hello, (hi))

WASMバイナリとABI定義の作成

$ cd contracts/hello
$ eosio-cpp -o hello.wasm hello.cpp --abigen
Warning, empty ricardian clause file
Warning, empty ricardian clause file
Warning, action <hi> does not have a ricardian contract
$ cd ../../

デプロイ用のアカウント作成

$ cleos wallet import --private-key 5KbmHJGdyb1WJCMCzeYXLY7ZwUSUAya1B2aYMrsDhE39tzGYQMD
imported private key for: EOS5xPkLMUhNBnzmWfu5X5Z7wrqkcNcanWZq3oGtZjwGEeaggtKjD
$ cleos create account eosio hello EOS5xPkLMUhNBnzmWfu5X5Z7wrqkcNcanWZq3oGtZjwGEeaggtKjD -p eosio@active
executed transaction: a43c46ae3e27a1b8c4e4ce0a38cae8158d5b85f597fbe71a4cdd71311d2ccbd6  200 bytes  1460 us
#         eosio <= eosio::newaccount            {"creator":"eosio","name":"hello","owner":{"threshold":1,"keys":[{"key":"EOS5xPkLMUhNBnzmWfu5X5Z7wrq...
warning: transaction executed locally, but may not be confirmed by the network yet         ] 

コントラクトのデプロイ

$ cleos set contract hello contracts/hello -p hello@active
Reading WASM from /home/ubuntu/contracts/hello/hello.wasm...
Publishing contract...
executed transaction: c8f27fc4ec0ce791b3da7a0c3f49175f5b47330ded2dc68d0ac58e6fd82df59a  1448 bytes  512 us
#         eosio <= eosio::setcode               {"account":"hello","vmtype":0,"vmversion":0,"code":"0061736d0100000001390b60027f7e006000017f60027f7f...
#         eosio <= eosio::setabi                {"account":"hello","abi":"0e656f73696f3a3a6162692f312e31000102686900010475736572046e616d650100000000...
warning: transaction executed locally, but may not be confirmed by the network yet         ] 

Note①: -pオプションは、<アカウント名>@の形式で指定します。
指定したアカウントのPermissionに設定されている秘密鍵でコントラクトのデプロイのトランザクションの署名を行います。
-pオプションが設定されていない場合、デフォルトで<コントラクト名>@が指定され、コントラクトのデプロイを行います。

Note②: コントラクト名とアカウント名は、一致させる必要があります。
一致しない場合、以下のエラーが発生します。

$ cleos set contract hello contracts/hello -p alice@active
Failed to get existing code hash, continue without duplicate check...
Reading WASM from /home/ubuntu/contracts/hello/hello.wasm...
Publishing contract...
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.
Error Details:
missing authority of hello
pending console output: 

トランザクションの発行例

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

おわりに

EOSは、他のパブリックブロックチェーンと比較すると、アカウントの仕様やトランザクション発行に伴う使用料等の考え方が大きく違っています。これは、Steemitの開発者のような、実際にブロックチェーンを用いてサービスを開発した人たちが仕様を決めていることが影響していると思われます。

今後もEOSについては引き続き調査していくため、特有の仕様に関する紹介記事を書いていく予定です。

参考

Contact

Contact