はじめに
この記事ではZilliqaというブロックチェーンのために設計されたScillaという言語を取り上げます。下記が分かっていれば読める記事かと思います。どうでしょうか。
- ブロックチェーンではコインを送り合うことができること。
- コインの送り合いにはそのブロックチェーン上にアカウントが必要なこと。
- アカウントにはざっくりいうと、単純にコインを送り合いたいユーザを表すアカウントと、コントラクトと呼ばれるブロックチェーン上で実行されるプログラムを表すアカウントの2つがあること。
この記事について
- コントラクトと呼ばれるブロックチェーン上で実行されるプログラムを扱う
- ScillaでHello Worldしてみて何となくScillaやブロックチェーンについて雰囲気がわかるのが目的。
- Zilliqaやブロックチェーン自体の説明はしない。
- 対象は何となくブロックチェーンがわかるプログラマ。
Scilla
-
Scillaはブロックチェーンの1つであるZilliqaのために設計された言語です。
- 他のブロックチェーンでは、例えばEthereumではSolidityという言語が使われます。
- 従来のブロックチェーンで起きた事件のいくつかは、プログラムのミスであることに着目し、言語レベルで安全性を担保する機能を実装する目的で設計されました。
- ScillaにはCoqで行われるような形式的検証の機能があります。従来は主に数学の証明のために使われてきたと思います。デプロイ後にソースを改変できないブロックチェーンでは最初にプログラムの検証が大事だということで形式的検証が言語レベルでサポートされているようです。
- 公式のブログが参考になります。
前提
- 私はブロックチェーンエンジニアではありません。
- Zilliqa、Scillaがどれくらい普及するかわかりません。
- Scillaの詳細な言語仕様は説明しません。
Hello World
ありがたいことに、テスト環境のWeb IDEを用意してくれています。パブリックなブロックチェーンなので普及のためだと思います。
さっそくWeb IDEを開いて見ましょう。
既にサンプルファイルが多数用意されています。
左ペインのHelloWorld.scilla
を選択すると下記のソースコードが表示されます。
(* HelloWorld contract *)
import ListUtils
(***************************************************)
(* Associated library *)
(***************************************************)
library HelloWorld
let one_msg =
fun (msg : Message) =>
let nil_msg = Nil {Message} in
Cons {Message} msg nil_msg
let not_owner_code = Int32 1
let set_hello_code = Int32 2
(***************************************************)
(* The contract definition *)
(***************************************************)
contract HelloWorld
(owner: ByStr20)
field welcome_msg : String = ""
transition setHello (msg : String)
is_owner = builtin eq owner _sender;
match is_owner with
| False =>
msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : not_owner_code};
msgs = one_msg msg;
send msgs
| True =>
welcome_msg := msg;
msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : set_hello_code};
msgs = one_msg msg;
send msgs
end
end
transition getHello ()
r <- welcome_msg;
msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; msg : r};
msgs = one_msg msg;
send msgs
end
必要なところを説明するとtransition setHello (msg : String)
やtransition getHello ()
は、String型の引数msg
を取るsetHello
メソッドと、引数を取らないgetHello
メソッドです。Scillaはtransition
という言葉を使っていますので今後トランジションと呼びましょう。field welcome_msg : String = ""
はString型で初期値が""
の変数で、フィールドと呼びます。これらのトランジションを使って、welcome_msg
フィールドの値を読み書きします。とりあえずサンプルファイルをテスト環境にデプロイしてみましょう。
サンプルファイルのデプロイ
右ペインのDEPLOYタブを選びます。Select account
をクリックするとデプロイに使うアカウントを自由に選ぶことができます。適当なアカウントを選択してください。Choose a scilla source file
にHelloWorld.scilla
を指定します。テスト環境では複数のアカウントが既に用意されていて、表示されているアカウントは、この記事を読まれている方によって別々だと思います。以後の画像では私の環境での文字列を表示しています。
Initialisation Parameters
にSelect account
でリストされたアカウントの中から適当なものを選んでコピペしてください。先ほどと同じアカウントでも構いません。DEPLOYボタンでデプロイします。
以下のようにデプロイの手数料(Gas)に53ZIL(Zilliqaで扱うコイン)がかかりました。
ちなみにテスト環境の全てのアカウントは初期状態で100000000ZILを持っていますが、デプロイに使ったアカウントを見てみると53ZIL減っています。
HelloWorld.scilla
の以下の部分を見てください。
(***************************************************)
(* The contract definition *)
(***************************************************)
contract HelloWorld
(owner: ByStr20)
これはコントラクト(ブロックチェーン上で実行されるプログラム)の宣言です。デプロイされるとHelloWorldコントラクトがブロックチェーン上に作成されて、イミュータブルなByStr20
型の変数owner
が初期化されます。初期値はデプロイ時にInitialisation Parameters
のところで指定したアカウントです。後に説明しますが、この時に指定したアカウントでしか、welcome_msg
フィールドの値を書き換えることができません。
デプロイしたコントラクトを見てみましょう。STATE
タブのContract State
を見てください。
initを見ると、ownerがInitialisation Parameters
のところで指定したアカウントで初期化されているのがわかるかと思います。便宜上、以後このアカウントをownerアカウントと呼びます。
setHelloトランジション
それでは次に、右ペインのCALL
タブを開いて、ownerアカウントでwelcome_msgフィールドの値を書き換えて見ましょう。
上から順にコントラクトを呼び出すアカウント(今回指定するのはownerアカウントです)、デプロイしたHellowWorldコントラクトを指定します。トランジションにsetHello
を指定します。下の方のTransition Parameters
にHello World!
と指定してCALL TRANSITION
ボタンを押して、setHelloトランジションを呼び出してみましょう。
成功すると以下のようになります。今回は手数料として243ZILがownerアカウントから使われたようです。
さっそく、HelloWorldコントラクトのwelcom_msgフィールドが書き換わったか見てみましょう。右ペインのSTATE
タブからContract State
のstate
を見ると、welcome_msgフィールドの値がHello World!
に書き換わっているのがわかるかと思います。state
の0
に_balance
という、作成時に初期化され、全てのコントラクトが持つフィールドがありますが、今回は説明を省きます。
改めてtransition setHello (msg : String)
を見てみます。
transition setHello (msg : String)
is_owner = builtin eq owner _sender;
match is_owner with
| False =>
msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : not_owner_code};
msgs = one_msg msg;
send msgs
| True =>
welcome_msg := msg;
msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : set_hello_code};
msgs = one_msg msg;
send msgs
end
end
is_owner = builtin eq owner _sender;
で変数is_owner
にコントラクト作成時に初期化したイミュータブルな変数owner
とHelloWorldコントラクトを呼び出したアカウント_sender
を比較した結果を格納(正確には式を束縛ですが)しています。
| False =>
の部分は関数型言語で使われるパターンマッチで、False
の場合の処理が書かれています。
今回はownerアカウントで呼び出したので、owner
と_sender
は一致しています。なので | True =>
の方にマッチして、welcome_msg := msg;
でwelcome_msgが書き換わり、msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : set_hello_code};
により、変数msg
にMessage型の値の {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : set_hello_code};
が格納され、msgs = one_msg msg;
により、変数msgs
にone_msg
関数を引数msg
で呼び出した結果が格納されます。one_msg
は以下のように記述されています。
let one_msg =
fun (msg : Message) =>
let nil_msg = Nil {Message} in
Cons {Message} msg nil_msg
これによりMessage型のリストが返されます。そして、最後にsend msgs
でコントラクトにMessage型のリストを送っています。
STATEタブで、welcome_msgフィールドにHello World!
が格納されたのが確認できると思います。本記事ではやりませんが、ownerアカウント以外で試すとwelcome_msgは書き換わりません。
getHelloトランジション
最後にgetHelloトランジションを呼び出してみましょう。CALLタブを開いて、上から順に任意のアカウントと、HelloWorldコンストラクタ、getHelloトランジションを指定します。
CALL TRANSION
ボタンまで押したらSTATE
タブを開いてみてください。今回はwelcome_msgフィールドを書き換えていないので、state
は変わりません。
getHelloトランジションのソースコードは以下のようになっています。
transition getHello ()
r <- welcome_msg;
msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; msg : r};
msgs = one_msg msg;
send msgs
end
ミュータブルな変数welcome_msgから変数r
に値を読み出して、変数msg
にMessage型の値、{_tag : "Main"; _recipient : _sender; _amount : Uint128 0; msg : r};
を入れてmsgs = one_msg msg;
でMessage型のリストを作り、send msgs
で送っています。送られたmsgs
はmessageLog
に表示されるので見てみましょう。
STATEタブのHelloWorldコントラクトのmessageLog
の1
が今送ったmsgs
です。ちなみに0
はさきほど、setHelloトランジションで送ったものです。
ちなみに、getHelloトランジションはwelcome_msgフィールドの値を読み出してmessageLogに値を書き込むというものでした。コンストラクトの呼び出しは取引が発生します。取引には手数料が必要です。また取引はブロックチェーンに書き込まれます。STATEタブを開いてコントラクトを表示するだけなら取引が発生していませんので、チェーンには何も書き込まれません。
おわりに
以上で簡単ではありますが、「ScillaでHello World」でした。現状Zilliqaはテストネットで運用されていますが、予定では2019年1月にメインネットに移行するそうです。ZilliqaはBitcoinやEthereumのスケーリングの問題を解決しています。例えば、メインネットへの参加ノード数によってはクレジットカード並の速度で決済ができるようです。詳細は公式サイトのドキュメントを御覧ください。それでは皆さん良いブロックチェーンライフを。
おまけ
コインとトークン
私はブロックチェーン初心者ですが、ネット上のいくつかの文献を見ると、仮想通貨という言葉がコインとトークンのどちらの意味でも使われていて混乱しました。コントラクトはプログラムなので、あるアカウントAが10000という値を持っているという表現をScillaで書けます。また、アカウントBに100送ったら、Aから100減らすみたいな処理を書いたとします。そのように処理を書いていくと最終的にZilliqa上で値の送り合いができますよね。この値をトークンと呼んでいるようです。トークンに対して、ここではZILに当たるものをコインもしくは、ネイティブトークンと呼ぶようです。恐らく仮想通貨はコインとトークンを包含した広い意味で使われているので、ITエンジニアがITエンジニア向けに書く記事においてはコインとトークンを使い分けてほしい気がします。
なぜトークンなのか
詳細を説明すると長くなるので、簡単に説明します。メインネットをローンチ後は、そのネットワークに参加するコンピュータ(ノード)が少数のうちは、チェーンが不安定だったり、悪さができたりします。なので、既に安定しているブロックチェーン上でトークンという形で値の送り合いをできる仕組みを作っているようです。
スマートコントラクト
この記事ではScillaで書かれたプログラムをコントラクトと表記しました。その意味でスマートコントラクトという言葉を使うのに違和感を覚えたので、スマートを省略しました。スマートコントラクトが概念を連想させるからなのか、もしくは紙の契約に対してということを強調する雰囲気だからでしょうか...。既に文脈がブロックチェーンの話で、具体的な実装を指すならコントラクトと言った方がなんかしっくりくるので、コントラクトと表記しました。ただスマートフォンのことをフォンと呼ばれても「?」となるので、省略しない方がいいでしょうか。フォンという言葉はすぐに電話一般を連想できるますが、コントラクトだとすぐに契約のイメージが湧きにくい日本人特有のものでしょうか。教えてください。