最近話題(?)のIOSTでスマートコントラクトを書いてみたので、頭の整理がてら記事書いてみます。
まずスマートコントラクトを書く前に次のものが必要です。
Docker(18.06CEより新しいやつ)
iWallet:IOSTのコマンドラインツールです。これでIOSTブロックチェーンとやり取りします。
公式ドキュメントを見てインストールしてみてください。
https://developers.iost.io/docs/ja/4-running-iost-node/iWallet.html
今回の流れは
- ローカルでテストノードを立てる。
- スマートコントラクト書く
- 実際にIOSTブロックチェーンとやり取りしてスマートコントラクトを試す。
という感じです。
1.ローカルテストノードを立ててみよう。
IOSTのDockerイメージをもとにDockerコンテナを作ってその中でノードを立てます。
まずターミナルを開いて次のコマンドを打ちます。
docker run --rm -d -p 30000-30003:30000-30003 iostio/iost-node
これでコンテナが立ち上がり、その中でブロックチェーンが動いてます。
次に下のコマンドでコンテナIDを調べて、
docker ps
それをコピーして下のコマンドの、ここだよ、に貼り付けます。<>は除いてください。
docker exec -it <ここだよ> /bin/bash
するとworkdir# が出てきてコンテナ内でシェルを開いた状態になります。
そこで下のようにコマンドを入力します。
adminはローカルテストネット用に公式が用意してくれているアカウントです。
終わったらexitと入力してコンテナ内を出ます。
root@containerID:/workdir# iwallet account import admin 2yquS3ySrGWPEKywCPzX4RTJugqRh7kJSo5aehsLYPEWkUxBWA39oMrZ7ZxuM4fgyXYs2cPwh5n8aNNpH5x2VyK1
とりあえずこれで諸々の準備は終わりました。
次からはコントラクトを書いていきます。
#### 2.スマートコントラクトを書く
では早速スマートコントラクトを書いてみましょう。
今回はユーザーインプットをブロックチェーンのストレージにID付きで保存。
それをIDで呼び出してみるよ、っていうコントラクトになっています。
まず前提として、IOSTのストレージにはJSON形式でデータが保存されます。
なので
ストレージに数値をしまいたければJSON.stringify()
ストレージから取り出した値を数値として使いたい場合はJSON.parse()して使う必要があります。
毎回書くとめんどくさいので最初にまとめて書いてしまいます。
長いコントラクトになればなるほど役立つと思います。
_get(k){
const val = JSON.parse(storage.get(k));
return val;
}
_put(k,v){
const val = JSON.stringify(v);
storage.put(k,val);
}
storage.put:ストレージにKey-Valueペアを保存
storage.get:ストレージからKeyをもとにValueを取ってくる。
などはIOSTのAPIです。
詳しくは公式ドキュメントのAPIページを見てください。
https://developers.iost.io/docs/en/3-smart-contract/IOST-Blockchain-API.html
では本題。
class TextRecorder {
init (){
//パブリッシュされたときに
// {"count":"0"}
// "text" っていう名前のmapに {"text_count_0":"GENESIS_TEXT"}って感じで保存。
this._put("count", 0);
storage.mapPut("text", "text_count_0" ,"GENESIS_TEXT");
}
//これはアップデート関数のテンプレートです
can_update(data) {
return blockchain.requireAuth(blockchain.contractOwner(), "active");
}
putText(txt){
//"count"のValueを持ってきて数値化。
let textId = this._get("count");
// それに1を足してそれが今回のテキストの番号になる
textId = textId + 1;
//テキストは”text"に{"text_count_#":"xxxx"}という感じのペアで保存される。
storage.mapPut("text", "text_count_" + textId, txt);
//最後にcountを再度JSON化してストレージに保存して今のテキストの数を記録する。
this._put("count", textId);
}
//今何個テキストが保存されてるかわかる。
getTextCount(){
return storage.get("text_count");
}
//テキストの番号をもとにストレージから対応するValueを返す。
getText(id){
let textId = "text_count_" + id;
let txt = storage.mapGet("text",textId);
return txt;
}
_get(k){
const val = JSON.parse(storage.get(k));
return val;
}
_put(k,v){
const val = JSON.stringify(v);
storage.put(k,val);
}
}
//abiファイルに出力する
module.exports = TextRecorder;
ざっくりまとめると、
テキストは"text_count_#":"xxxx"というのペアで保存される。
それとは別に"count":"xx"というペアがありputTextの度に”xx”の値が1ずつ増える。
こんな感じでストレージを使ってテキストに対してユニークな番号を割り当ててます。
スマートコントラクトを試す
jsファイルをコンパイルしてabiファイルを生成します。
abiファイルは、IOSTネットワーク上のスマートコントラクトを呼び出すためのインターフェースです。
ここでトランザクションを受付して指定されたコントラクトを呼び出してくれるわけです。
iwallet compile trecorder.js
jsファイル,abiファイルをブロックチェーン上にパブリッシュして乗っけます。最後の行のコントラクトIDは取っておいてください。
iwallet publish -a admin trecorder.js trecorder.js.abi
関数の呼び出しはiwallet callを使いますコントラクトID、関数名を指定して引数は配列の形で渡します。全部文字列です。
iwallet call -a admin contractID "関数名" '["引数"]'
まずgetText関数でしっかりtext_count_0の”GENESIS_TEXT"がストレージに保存されているか見てみます。
iwallet call -a admin Contract5YykenC3DqcgQbZt6m1Gis8vLuGK1p63mEwfVJSrFP1h "getText" '["0"]'
しっかり保存されています。
じゃあ次はputTextで適当にテキストを保存してみます。
iwallet call -a admin Contract5YykenC3DqcgQbZt6m1Gis8vLuGK1p63mEwfVJSrFP1h "putText" '["Hello IOST"]'
で、またgetTextで呼び出しますが、今度は引数に1を渡します。
すると
こんな感じでしっかりtextのtext_count_1にHello IOSTが保存されていますね。
ちなみにコントラクトのアップデートは、jsファイルを再度コンパイルして次のコマンドを打ちます。
iwallet publish -a admin trecorder.js trecorder.js.abi --update コントラクトID
####まとめ
IOSTの初歩的なスマートコントラクトをいじってみました。
まだまだ開発環境が整っていなかったり文献が少ないのでわからないこと多々です。
今度はUIとつなげてDApp的なもの作ってまた記事にしようと思います。
おしまい