ScillaでHello World


はじめに

この記事では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.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 fileHelloWorld.scillaを指定します。テスト環境では複数のアカウントが既に用意されていて、表示されているアカウントは、この記事を読まれている方によって別々だと思います。以後の画像では私の環境での文字列を表示しています。

Initialisation ParametersSelect accountでリストされたアカウントの中から適当なものを選んでコピペしてください。先ほどと同じアカウントでも構いません。DEPLOYボタンでデプロイします。

以下のようにデプロイの手数料(Gas)に53ZIL(Zilliqaで扱うコイン)がかかりました。

ちなみにテスト環境の全てのアカウントは初期状態で100000000ZILを持っていますが、デプロイに使ったアカウントを見てみると53ZIL減っています。

HelloWorld.scillaの以下の部分を見てください。

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 ParametersHello World!と指定してCALL TRANSITIONボタンを押して、setHelloトランジションを呼び出してみましょう。

成功すると以下のようになります。今回は手数料として243ZILがownerアカウントから使われたようです。

さっそく、HelloWorldコントラクトのwelcom_msgフィールドが書き換わったか見てみましょう。右ペインのSTATEタブからContract Statestateを見ると、welcome_msgフィールドの値がHello World!に書き換わっているのがわかるかと思います。state0_balanceという、作成時に初期化され、全てのコントラクトが持つフィールドがありますが、今回は説明を省きます。

改めてtransition setHello (msg : String)を見てみます。

HelloWorld.scilla
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;により、変数msgsone_msg関数を引数msgで呼び出した結果が格納されます。one_msgは以下のように記述されています。

HelloWorld.scilla
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トランジションのソースコードは以下のようになっています。

HelloWorld.scilla
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で送っています。送られたmsgsmessageLogに表示されるので見てみましょう。

STATEタブのHelloWorldコントラクトのmessageLog1が今送った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で書かれたプログラムをコントラクトと表記しました。その意味でスマートコントラクトという言葉を使うのに違和感を覚えたので、スマートを省略しました。スマートコントラクトが概念を連想させるからなのか、もしくは紙の契約に対してということを強調する雰囲気だからでしょうか...。既に文脈がブロックチェーンの話で、具体的な実装を指すならコントラクトと言った方がなんかしっくりくるので、コントラクトと表記しました。ただスマートフォンのことをフォンと呼ばれても「?」となるので、省略しない方がいいでしょうか。フォンという言葉はすぐに電話一般を連想できるますが、コントラクトだとすぐに契約のイメージが湧きにくい日本人特有のものでしょうか。教えてください。