IOSTブロックチェーンでスマートコントラクトを書く その1
IOST スマートコントラクトの基本的な書き方
(Mainnetリリース後の2019年3月10日更新)
前提条件
- Docker 最新版のインストール
- Docker Community Editionを公式サイトからインストールして、dockerコマンドが動作することを確認しておいてください。
Windows版 Mac版
テスト環境のインストール
- テストノードの起動とiwalletの確認
- コマンドプロンプトまたはターミナルを開いて、次のようにコマンドを入力して、インストールと実行を開始します。ここでは、追加の開発ツールもインストールされたバージョンを使います。
$ docker run -it --rm -d -p 30000-30003:30000-30003 iostio/iost-node:2.5.0-dbb56b7
次のような表示がされて、インターネットから必要なファイルをインストールします。この場合、バックグランドで起動します。
Unable to find image 'iostio/iost-node:2.5.0-dbb56b7' locally
2.5.0-dbb56b7: Pulling from iostio/iost-node 7b8b6451c85f: Already exists
ab4d1096d9ba: Already exists e6797d1788ac: Already exists e25c5c290bde: Already exists dd95d199336e: Pull complete 633f38155706: Pull complete 8ed9f2869f77: Pull complete 6607d0d46585: Pull complete edfae8fa2cfc: Pull complete 8f987566e055: Pull complete 8bd88b56ce83: Pull complete d3ec982a30ca: Pull complete 63e060801f11: Pull complete b8b436dccc06: Pull complete fc20f66d744a: Pull complete
Digest: sha256:6f8fd924838a96c2fa851488af48c9b3cc1cad1a62f267690776f8cb5ca9a362
Status: Downloaded newer image for iostio/iost-node:2.5.0-dbb56b7 3e90179c24eac3b825fc47da258846ffee69e40db443149d340152a0f6403751
次のようにコマンドを入力して、インストール状態を確認します。
$ docker ps
次のような表示がされますので、コンテナID(CONTAINER ID)を確認してください。 この例では、 dbaaee34942c がコンテナIDです。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3e90179c24ea iostio/iost-node:2.5.0-dbb56b7 "iserver -f /var/lib…" 4 minutes ago Up 4 minutes 0.0.0.0:30001-30002->30001-30002/tcp lucid_feynman
コンテナを実行します。
$ docker exec -it <表示されたコンテナID> /bin/bash
これ以降はDocker内で作業します。
コンテナを実行した画面が次のような状態になっていることを確認してください。これ以降はroot@453c72249832:/workdirの部分の表示は省略します。# の後ろが入力するコマンドになります。
root@453c72249832:/workdir#
プログラムを編集を用意にするためにnanoエディタをインストールします。viはインストール済みですので、viでよれけばそのまま使えます。他のアプリもどう
# apt update
Get:1 http://archive.ubuntu.com/ubuntu xenial InRelease [247 kB]
・・・(省略)
# apt install nano
Reading package lists... Done
・・・(省略)
これで、事前の設定は完了です。
Dockerkコンテナ内で、 iwallet コマンドが実行できることを確認します。
# iwallet -h
An IOST RPC client
Usage:
iwallet [command]
・・・(省略)
管理者アカウントadminの秘密鍵をインポートします。 adminの後ろが鍵です。長いですが、全体を1行で入力をしてください。
# iwallet account import admin 2yquS3ySrGWPEKywCPzX4RTJugqRh7kJSo5aehsLYPEWkUxBWA39oMrZ7ZxuM4fgyXYs2cPwh5n8aNNpH5x2VyK1
HelloWorld
HelloWorldコントラクトの作成
viエディタで新規コントラクトを作成します。
# nano helloworld.js
次のようにコードを入力します。IOSTのスマートコントラクトは、JavaScriptで記述します。ただし、すべての機能が使えるわけではありませんので、注意してください。
class HelloWorld {
init() {}
hello(someone) {
return "hello, "+ someone
}
}
module.exports = HelloWorld;
ここで、init関数は、初期化のための関数です。ここでは何もしていません。hello関数は、渡された文字列をつけて、「hello, <渡された文字列>」と表示する関数です。
nanoエディタでは、画面の下部にコマンドが表示されていますので、それを参考にしてください。^Xは、Ctrlキーを押しながらXキーを押すことを意味しています。
HelloWorldコントラクトのコンパイル
次のようなコマンドを入力して、HelloWorldコントラクトをコンパイルします。
# iwallet compile helloworld.js
成功すれば、次のように表示されます。されない場合は、プログラムを見直してください。
Successfully generated abi file as: helloworld.js.abi
コントラクトをコンパイルすると、ABI(Application Binary Interface)ファイルが生成されます。これは、コントラクトのインターフェースの情報を記述したものです。
生成された helloworld.js.abi を確認します。
# cat helloworld.js.abi
ファイルの内容は、次のようなJSON形式になっています。
{
"lang": "javascript",
"version": "1.0.0",
"abi": [
{
"name": "hello",
"args": [
"string"
],
"amountLimit": [],
"description": ""
}
]
}
HelloWorldコントラクトのテストノードへのパブリッシュ
次のようなコマンドを入力して、HelloWorldコントラクトをテストネットへパブリッシュします。これによって、コントラクトがブロックチェーンに送られて格納されます。
# iwallet --account admin publish helloworld.js helloworld.js.abi
成功すれば、結果は次のようになります。
Sending transaction...
Transaction has been sent.
The transaction hash is: 6utckwCQXoJQ1wDRdWM4V62hBRuMnmohfYJCUgp9176p
Checking transaction receipt...
SUCCESS!
The contract id is: Contract4nb7JeLK9RJpE4pvbcFmn3TFdmsibnY3Zu22jaPmSSgo
最後に表示された Contract4nb7JeLK9RJpE4pvbcFmn3TFdmsibnY3Zu22jaPmSSgo がコントラクトIDです。
これを使って、テストネットにあるHelloWorldコントラクトを呼び出すことができます。
HelloWorldコントラクトの呼び出し
次のようなコマンドを入力して、HelloWorldコントラクト内の hello 関数に「IOST」という文字列を渡して呼び出します。
# iwallet --account admin call "<表示されたコントラクトID>" "hello" '["IOST"]'
成功すれば、結果は次のようになります。
Sending transaction...
Transaction has been sent.
The transaction hash is: BLJj3zcpnhNZ3ud9FFARYBRiJqsrKdyz6CMFj1eaHsUD
Checking transaction receipt...
SUCCESS!
この例で、 BLJj3zcpnhNZ3ud9FFARYBRiJqsrKdyz6CMFj1eaHsUD がトランザクションIDです。これを使って、後からでもトランザクションの結果を調べることができます。
- レシートの取得
実行結果を確認するために、次のようなコマンドを入力して、レシートを取得します。これは、いつでもできます。
# iwallet receipt <トランザクションID>
成功すれば、結果はつぎのようになります。
{
"txHash": "BLJj3zcpnhNZ3ud9FFARYBRiJqsrKdyz6CMFj1eaHsUD",
"gasUsage": 33745,
"ramUsage": {
},
"statusCode": "SUCCESS",
"message": "",
"returns": [
"[\"hello, IOST\"]"
],
"receipts": [
]
}
returnsのところに表示されているのが実行して戻ってきた値で、呼び出した時にHelloWorldコントラクトに渡した「IOST」が追加されて、「hello, IOST」に結果がなっているのがわかります。それ以外にもガスの使用量とかも見ることができます。
ストレージを使う
ストレージを使うと、ブロックチェーン上にデータを保存することができます。ストレージは、コントラクトごとになり、他のコントラクトからはアクセスできません。ストレージは、IOSTで用意されているstorageクラスの関数を使用して操作できます。値の書き込みには、put関数、読み出しにはget関数を使用します。
- storage.put(“キー”, “値”);
- const value = storage.get(“キー”);
Storageコントラクトの作成
viエディタで新規コントラクトを作成します。
# nano storage.js
次のように編集します。ここでは、初期値として、”TEST”という文字列をinit関数で格納しています。そして、read関数でその値を取り出し、change関数で値を変更しています。
class Storage {
init() {
storage.put("value1", "TEST")
}
read() {
return storage.get("value1")
}
change(someone) {
storage.put("value1", someone)
}
}
module.exports = Storage;
Storageコントラクトのコンパイル
次のようなコマンドを入力して、Storageコントラクトをコンパイルします。
# iwallet compile storage.js
成功すれば、次のように表示されます。されない場合は、プログラムを見直してください。
Successfully generated abi file as: storage.js.abi
生成された storage.js.abi を確認します。
# cat storage.js.abi
ファイルの内容は、次のようなJSON形式になっています。
{
"lang": "javascript",
"version": "1.0.0",
"abi": [
{
"name": "read",
"args": [],
"amountLimit": [],
"description": ""
},
{
"name": "change",
"args": [
"string"
],
"amountLimit": [],
"description": ""
}
]
}
Storageコントラクトのパブリッシュ
# iwallet --account admin publish storage.js storage.js.abi
成功すれば、結果は次のようになります。
Sending transaction...
Transaction has been sent.
The transaction hash is: Gyof83SQYsFfSUYMHk5ChqMuoYtn8zTwmfq9aY9XdHoo
Checking transaction receipt...
SUCCESS!
The contract id is: Contract3ugRgjd6AXc8CNLvfCSiPTBacyooUN6syzhrFffRf2Wk
コントラクトIDを確認してください。この例では、コントラクトIDは、 Contract3ugRgjd6AXc8CNLvfCSiPTBacyooUN6syzhrFffRf2Wk です。
最初の状態のストレージの確認
ストレージの最初の状態を確認します。
# iwallet --account admin call "<コントラクトID>" "read" '[]'
成功すれば、結果は次のようになります。
Sending transaction...
Transaction has been sent.
The transaction hash is: 7zMhgUuzzeowX9CkTgTffHU5fkSF17g5XBjbqTe8iwHG
Checking transaction receipt...
SUCCESS!
この例では、トランザクションIDは、 7zMhgUuzzeowX9CkTgTffHU5fkSF17g5XBjbqTe8iwHGです。 レシートを確認します。
# iwallet receipt "<トランザクションID>"
結果は次のように、「TEST」が入っていることがわかります。これは、init関数でストレージに格納されたものです。
{
"txHash": "7zMhgUuzzeowX9CkTgTffHU5fkSF17g5XBjbqTe8iwHG",
"gasUsage": 33791,
"ramUsage": {
},
"statusCode": "SUCCESS",
"message": "",
"returns": [
"[\"TEST\"]"
],
"receipts": [
]
}
ストレージの変更
# iwallet --account admin call "<コントラクトID>" "change" '["HELLO"]'
成功すれば、結果は次のようになります。
Sending transaction...
Transaction has been sent.
The transaction hash is: G4GNxTWfGwHA1fhZfuUDRnW6nVKMqDhkCwZjfjhxQuLZ
Checking transaction receipt...
SUCCESS!
変更後のストレージの確認
# iwallet --account admin call "<コントラクトID>" "read" '[]'
成功すれば、結果は次のようになります。
Sending transaction...
Transaction has been sent.
The transaction hash is: G4GNxTWfGwHA1fhZfuUDRnW6nVKMqDhkCwZjfjhxQuLZ
Checking transaction receipt...
SUCCESS!
この例では、トランザクションIDは、 G4GNxTWfGwHA1fhZfuUDRnW6nVKMqDhkCwZjfjhxQuLZ
です。 レシートを確認します。
# iwallet receipt "<トランザクションID>"
結果は次のように、「HELLO」が入っていることがわかります。
{
"txHash": "G4GNxTWfGwHA1fhZfuUDRnW6nVKMqDhkCwZjfjhxQuLZ",
"gasUsage": 33801,
"ramUsage": {
},
"statusCode": "SUCCESS",
"message": "",
"returns": [
"[\"HELLO\"]"
],
"receipts": [
]
}