はじめに
本記事ではEOSのスマートコントラクトの開発環境を構築し、Helloworldコントラクトを作成し、実行する体験を行います。
本記事はUbuntu18.04上で環境構築を行っていきます。
本記事は、過去にコンセンサス・ベイスが主宰していたオンラインサロンの記事です。記事は2017年~2018年にかけて執筆されたため、一部は、既に古くなっている可能性があります。あらかじめご了承ください。
関連する記事
EOSIO Developer Portal 解説 最短でHello World!
EOSIO Developer Portal 解説 独自トークンの発行 第1回
EOSIO Developer Portal 解説 独自トークンの発行 第2回
EOSIO Developer Portal 解説 独自トークンの発行 第3回
EOSIO Developer Portal 解説 データの永続化とインラインアクション 第1回
EOSIO Developer Portal 解説 データの永続化とインラインアクション 第2回
EOSIO Developer Portal 解説 データの永続化とインラインアクション 第3回
EOSとは
EOSとはスケーラブルな分散型アプリケーションプラットフォームです。競合としてイーサリアムが挙げられますが、EOSはイーサリアムにおけるスケーラビリティ問題をイーサリアムとは別の方法で解決しようとしています。
EOSで特徴的なのはDelegated Proof of Stake(DPOS)というコンセンサスアルゴリズムです。
DPOSはネットワーク参加者の投票によってブロック生成者を選び、その選ばれたブロック生成者が実際にブロックを作成していくという仕組みのコンセンサスアルゴリズムです。
PoWとは違い、計算力を必要としないので、ブロックの生成間隔は単なる変数であり、この変数を小さくすることで、秒間に処理できるトランザクションを増やすことができます。(セキュリティ上の問題が指摘される場合もある)
イントロダクション
ここからは、EOS developersの資料に従って進行していきます。
https://developers.eos.io/eosio-home/docs/introduction
この資料から学べるのは以下のことです。
・どのようにしてノードを立ち上げるか?
・ウォレットと秘密鍵の管理
・アカウントの作成
・コントラクトを作る
・コントラクトの編集
・コントラクトのデプロイ
EOSのコントラクトはC++によって書かれるので、若干のC++の知識を要求されますが、本記事のHelloworldに関しては、その知識がなくても理解できると思います。
HelloWorldまでの環境構築
Dockerのインストール
EOSのノードはDocker上で動作するので、まずDockerをインストールします。
$ sudo apt-get update
$ sudo apt-get install apt-transport-https ca-certificates curl software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable test edge"
$ sudo apt-get update
$ sudo apt-get install docker-ce
なお、macOS / WindowsへのDockerインストールは下記の公式サイトから可能です。https://www.docker.com/get-started
ちゃんとインストールされたかどうかを確認します。
$ docker --version
作業ディレクトリの作成
任意のディレクトリに作業ディレクトリを作成します。今後の作業はこのディレクトリ内で行います。
$ mkdir contracts
$ cd contracts
ディレクトリのフルパスを控えておきます。
$ pwd
ノードの立ち上げ
EOSIOのDockerイメージをダウンロードします。
$ docker pull eosio/eos:v1.4.2
dockerに引数を渡してノードを起動します。フルパスと書いている部分は先程の作業ディレクトリのフルパスを入れてください。
$ docker run --name eosio \
> --publish 7777:7777 \
> --publish 127.0.0.1:5555:5555 \
> --volume フルパス:フルパス \
> --detach \
> eosio/eos:v1.4.2 \
> /bin/bash -c \
> "keosd --http-server-address=0.0.0.0:5555 & exec nodeos -e -p eosio --plugin eosio::producer_plugin --plugin eosio::chain_api_plugin --plugin eosio::history_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:7777 --access-control-allow-origin=* --contracts-console --http-validate-host=false --filter-on='*'"
※例えば「–volume /home/user1/contracts:/home/user1/contracts」と記述すれば、「docker run」の後、docker外で「/home/user1/contracts/」配下にファイルやフォルダを生成すると、dockerコンテナ内にもファイルやフォルダが自動複製される様になります。
無事に起動したかどうかを確かめます。
$ docker logs --tail 10 eosio
次のようなログが出力されれば成功です。
1929001ms thread-0 producer_plugin.cpp:585 block_production_loo ] Produced block 0000366974ce4e2a... #13929 @ 2018-05-23T16:32:09.000 signed by eosio [trxs: 0, lib: 13928, confirmed: 0]
1929502ms thread-0 producer_plugin.cpp:585 block_production_loo ] Produced block 0000366aea085023... #13930 @ 2018-05-23T16:32:09.500 signed by eosio [trxs: 0, lib: 13929, confirmed: 0]
1930002ms thread-0 producer_plugin.cpp:585 block_production_loo ] Produced block 0000366b7f074fdd... #13931 @ 2018-05-23T16:32:10.000 signed by eosio [trxs: 0, lib: 13930, confirmed: 0]
1930501ms thread-0 producer_plugin.cpp:585 block_production_loo ] Produced block 0000366cd8222adb... #13932 @ 2018-05-23T16:32:10.500 signed by eosio [trxs: 0, lib: 13931, confirmed: 0]
1931002ms thread-0 producer_plugin.cpp:585 block_production_loo ] Produced block 0000366d5c1ec38d... #13933 @ 2018-05-23T16:32:11.000 signed by eosio [trxs: 0, lib: 13932, confirmed: 0]
1931501ms thread-0 producer_plugin.cpp:585 block_production_loo ] Produced block 0000366e45c1f235... #13934 @ 2018-05-23T16:32:11.500 signed by eosio [trxs: 0, lib: 13933, confirmed: 0]
1932001ms thread-0 producer_plugin.cpp:585 block_production_loo ] Produced
block 0000366f98adb324... #13935 @ 2018-05-23T16:32:12.000 signed by eosio [trxs:
Dockerコンテナ内に入る方法/ウォレットの確認
次のコマンドを入力すれば、dockerコンテナ(eosio)内にbash-shellでログインする事が出来ます。
$ docker exec -it eosio bash
(なお、docker execコマンドでは任意のシェルを指定できますが、EOS公式サイトの手順ではbashが使用されているため、本記事でもそれにならいシェルをbashに指定しています。)
$ docker exec -it eosio bash
#
次のコマンドを入力すると、以下のような結果が出力されます。
# cleos --wallet-url http://127.0.0.1:5555 wallet list
Wallets:
[]
最後に一旦dockerコンテナ(eosio)から出ます。
# exit
$
エイリアスの設定
cleosコマンドは、dockerコンテナ(eosio)内にインストールされているため、dockerの外側からは実行できません。そこでエイリアスを設定し、dockerコンテナ(eosio)内に入らなくてもcleosコマンドが使える様にします。
エイリアス設定の為、vimでbashrcファイルを編集します。
$ vim ~/.bashrc
.bashrcファイルの末尾に以下のコードを追加します。
alias cleos='docker exec -it eosio /opt/eosio/bin/cleos --url http://127.0.0.1:7777 --wallet-url http://127.0.0.1:5555'
※Linux(Ubuntu)環境でdocker実行に sudo が必要な場合:
alias cleos='sudo docker exec -it eosio /opt/eosio/bin/cleos --url http://127.0.0.1:7777 --wallet-url http://127.0.0.1:5555'
bashrcの変更を適用します。
$ source ~/.bashrc
うまく設定できていれば、eosioのDockerコンテナに入らなくても、外からcleosコマンドが実行できます。
$ cleos wallet list
Wallets:
[]
CDTのインストール
CDTはEOSのコントラクトの開発環境です。それではCDTをインストールして行きます。
$ wget https://github.com/eosio/eosio.cdt/releases/download/v1.3.2/eosio.cdt-1.3.2.x86_64.deb
$ sudo apt install ./eosio.cdt-1.3.2.x86_64.deb
※macOSをお使いの場合は、Homebrewを使ってインストールできます。
$ brew tap eosio/eosio.cdt
$ brew install https://raw.githubusercontent.com/EOSIO/homebrew-eosio.cdt/50f00447765854f6e4e3b2d4ef36324cc38e5362/eosio.cdt.rb
その他のOSでのインストール方法は下記のページをご覧ください。
EOS Developers – Install the CDT
今回はこのeosio.cdtのうち、C++で記述されたソースをコンパイルするためのeosio-cppを主に使用します。下記コマンドでcdtが正常にインストールされたか確認しましょう。
$ eosio-cpp --version
eosio-cpp version 1.3.2
このように、eosio-cppのバージョンが表示されれば成功です。
開発用ウォレットの作成
次のコマンドを入力します。
$ 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.
"PW5Kewn9L76X8Fpd....................t42S9XCw2"
次にウォレットを開きます。
$ cleos wallet open
次のようなメッセージが出れば成功です。
Opened: default
ウォレットのリストを確認します。
$ cleos wallet list
次のようなメッセージが出れば成功です。
Wallets:
[
"default"
]
次にウォレットをアンロックします。次のコマンドを入力したあと、先程控えておいたパスワードを入力します。
$ cleos wallet unlock
password:
次にまたウォレットのリストを確認してみます。
$ cleos wallet list
ウォレットの表示が少し変わっていることがわかります。
Wallets:
[
"default *"
]
次に公開鍵をウォレットから取得します。
$ cleos wallet create_key
次のようなメッセージが出れば成功です。公開鍵が表示されるので、控えておきます。
Created new private key with a public key of: "EOS8PEJ5FM42xLpHK...X6PymQu97KrGDJQY5Y"
次に開発者キーをウォレットに取り込みます。
$ cleos wallet import
private Key:
開発者キーを入力するように要求されます。
ドキュメント上で公開されている開発者キーは
5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
なのでこれを入力します。
次のようなメッセージが出れば成功です。
private key: imported private key for: EOS6MRy…..ET5GDW5CV
※なお、本番環境ではこの開発者キーは絶対に使用しないようご注意ください。アカウントへのアクセスを失うなど重大な事態に陥る危険性があります。
テスト用アカウントの作成
テスト用にbobとaliceの2つのアカウントを作成します。以下のコマンドを入力します。
(Googleドキュメントの表示の都合で改行しているように見えることがありますが、$ 以下のコマンドはそれぞれ一行で入力、実行してください)
$ cleos create account eosio bob YOUR_PUBLIC_KEY
$ cleos create account eosio alice YOUR_PUBLIC_KEY
*YOUR_PUBLIC_KEY の部分には$cleos wallet create_key コマンドで生成した公開鍵を入力してください。
次のようなメッセージが出れば成功です。
executed transaction: 40c605006de... 200 bytes 153 us
# eosio <= eosio::newaccount {"creator":"eosio","name":"alice","owner":{"threshold":1,"keys":[{"key":"EOS5rti4LTL53xptjgQBXv9HxyU...
warning: transaction executed locally, but may not be confirmed by the network yet ]
Hello World!
EOSの最小構成のコントラクトを実行していきます。まずcontractsディレクトリ以下にhelloディレクトリを作成します。そしてコントラクトのファイルを作成します。
$ mkdir hello
$ cd hello
$ touch hello.cpp
次にhello.cppをお好みのエディタで開いて次のソースコードを入力します。
#include
#include
using namespace eosio;
class hello : public contract
{
public:
using contract::contract;
[[eosio::action]] void hi(name user) {
require_auth(user);
print("Hello, ", name{user});
}
};
EOSIO_DISPATCH(hello, (hi))
次にこのコードをコンパイルします。
$ eosio-cpp -o hello.wasm hello.cpp --abigen
コンパイルに成功すると、.abi, .wasmという拡張子のファイルが新たに生成されます。
下記コマンドで確認してみましょう。
$ ls -l
hello.abi
hello.cpp
hello.wasm
次にコントラクト実行用のアカウントを作成します。公開鍵の部分には先程控えておいた公開鍵を入力します。
$ cleos create account eosio hello 公開鍵 -p eosio@active
次にコントラクトをブロックチェーン上に公開します。contractsのフルパスには、作業領域のフルパスを入力します。
$ cleos set contract hello contractsのフルパス/hello -p hello@active
それでは、コントラクトの動作確認を行っていきましょう。
$ cleos push action hello hi '["bob"]' -p bob@active
executed transaction: e0a031671e9f14ac7e4540bc12a62d2673784afd8df9710dfb61d2733ee513c8 104 bytes 982 us
# hello <= hello::hi {"user":"bob"}
>> Hello, bob
warn 2018-11-11T02:37:45.942 thread-0 main.cpp:482 print_resultwarning: transaction executed locally, but may not be confirmed by the network yet
$ 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.
ここでは、正常ケース
cleos push action hello hi '["bob"]' -p bob@active
と、異常ケース
cleos push action hello hi '[bob"]' -p alice@active
の2つのパラメータでコントラクトを実行しました。このコントラクトの内容について次のセクションで解説します。
HelloWorld!の解説
#include
#include
using namespace eosio;
class hello : public contract
{
public:
using contract::contract;
[[eosio::action]] void hi(name user) {
require_auth(user);
print("Hello, ", name{user});
}
};
EOSIO_DISPATCH(hello, (hi))
このコントラクトのコードは、コントラクト実行時に引数を一つ取り、その引数の値が、そのアカウントのユーザ名と同じなら「Hello ユーザ名」と返すコントラクトです。
ですから、次のコントラクトを実行するとHelloが帰ってきて、
$ cleos push action hello hi '["bob"]' -p bob@active
executed transaction: e0a031671e9f14ac7e4540bc12a62d2673784afd8df9710dfb61d2733ee513c8 104 bytes 982 us
# hello <= hello::hi {"user":"bob"}
>> Hello, bob
warn 2018-11-11T02:37:45.942 thread-0 main.cpp:482 print_resultwarning: transaction executed locally, but may not be confirmed by the network yet
次のコントラクトを実行するとエラーが帰ってきます。
$ 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.
まとめ
今回は公式のドキュメントをもとに簡単なEOSのコントラクトを体験しました。
また、HelloWorldコントラクトを実行する際に、手数料の支払いやトークンのやり取り等を行っていないことがわかったかと思います。EOSの手数料が無料と言われているのはこういうところから来ているのではないでしょうか。