Tezos ブロックチェーン のためのスマートコントラクト記述言語 SCaml、チュートリアルの第5回目です。
今回はついに SCaml でTezosのスマートコントラクトを書きます!
- コンセプトの紹介(英語)
- リリースのお知らせ(英語)
- プロジェクトページ
- レポジトリ
- 第0回: コンセプトの説明
- 第1回: 準備
- 第2回: 概観
- 第3回: ウォレット操作
- 第4回: スマートコントラクトのデータ型
- 第5回: はじめての SCaml コントラクト
- 第6回: もう少し複雑なコントラクト。パラメータとストレージ
- 第7回: 投票コントラクトを書いてみる
やっと SCaml
やっとです。ブロックチェーンは事前に仮定する情報が多すぎるよね。
SCaml とは
OCaml で書かれたスマートコントラクトのコードを Tezos の Michelson VM のコードにコンパイルします。言語仕様は OCaml のサブセットです。OCaml を知っていればすぐ書けますし、OCaml の既存の本や資料を読めば SCaml は書けます。OCaml は日本の大学の多くのコンピュータ科学専攻の授業でもプログラミング実習などに使われている良い言語です。
SCaml のプログラムは OCaml のプログラムでもあるので、OCaml コンパイラでコンパイルしてやれば Michelson だけでなく、普通の実行ファイルにコンパイルすることもできますし、JSOO を使うと JavaScript にも落とせます。スマートコントラクトだけでなく、サーバーサイド、クライアントサイドの dApps 開発局面の全てでコードを共通化できます。
また、Coq や F* などの形式検証のための言語とも相性が良く、検証されたスマートコントラクトの開発への可能性を持っています。
とりあえずやってみよ
最も簡単な SCaml のスマートコントラクトです。何もしないコントラクトです:
- 入力
- パラメータは特にないので
()
を使います。 - ストレージも特にありません。 やはり
()
を使います。
- パラメータは特にないので
- 出力
- ブロックチェーンに関して何も行いません。ですので operation は空のリスト
[]
です。 - ストレージは変化しませんので
()
のままです。
- ブロックチェーンに関して何も行いません。ですので operation は空のリスト
ホントつまらないコントラクトですが、これを SCaml で書くと次のようになります:
open SCaml
let [@entry] main () () =
([], ())
open SCaml
- SCaml の基本ライブラリに定義された名前空間へのアクセス宣言です。SCaml のスマートコントラクトではまず付けておけば問題ありません。SCaml基本ライブラリを見ればどんな関数が用意されているかわかります。
[@entry]
let [@entry]
はスマートコントラクトとして呼び出せる関数「エントリ」の定義に使います。[@entry]
アトリビュートのないlet
宣言はコントラクトとして外部から呼び出すことができません。[@...]
は OCaml のアトリビュートと呼ばれるもので、プリプロセッサや SCaml コンパイラのような OCaml コードを処理するプログラムに処理のヒントを与えるために使われます。- エントリ入力:
let [@entry] main () () = ...
- エントリ関数は引数を二つ取ります。第一引数は呼び出し時に与えられるパラメータです。第二引数はストレージの中身が与えられます:
let [@entry] エントリ名 パラメータ = ...
- エントリ出力
- エントリの出力は operation と新ストレージの組みです。ここでは、何も operation は空(
[]
)で、ストレージは()
のままなので、([], ())
になります。 ストレージは入力も出力も二番目に来ると思えばいいでしょう。
そんな感じです。わかりましたか?
コンパイルしてみる
上のコードをエディタで simplest.ml
という名前で作成して、SCaml でコンパイルしてみましょう:
$ ./scamlc simplest.ml
Linking Simplest
Compiling simplest Simplest_0
$
Docker スクリプトの都合上、simplest.ml
は scamlc
スクリプトと同じディレクトリに置いてください。
成功すれば、同じディレクトリに simplest.tz
という Michelson のファイルができているはずです:
$ cat simplest.tz
parameter unit ;
storage unit ;
code { DROP # let param_storage_164 = (input, storage)
# free param_storage_164
; UNIT # let _unit_181 = ()
; NIL operation # let _ops_182 = []
; PAIR # let _pair_183 = (_ops_182, _unit_181)
# return _pair_183
}
うまくいかなかった場合は、あなたが OCaml/SCaml のプログラムのどこかでミスタイプしたからかもしれません。原因を探すより、とりあえず今は、上のコードをコピペしてコンパイルしてみてください。
デプロイする
このコンパイルされた Michelson コードを Tezos のネットワーク上にスマートコントラクトとしてデプロイ(インストールのかっこいい言い方)します。超長いコマンドです:
./tezos-client originate contract simplest \
transferring 0 from myself \
running simplest.tz \
--burn-cap 100
- デプロイされたスマートコントラクトはウォレット上で
simplest
というエイリアス名をつけます myself
アカウントから初期トークンとして0
tezzy 送ります- スマートコントラクトの Michelson コードは
simplest.tz
に入っています - ストレージ確保のための費用として最大
100
支払います。適当な値です。(本当はほんのちょっとしかかりません)
$ ./tezos-client originate contract simplest transferring 0 from myself running simplest.tz --burn-cap 100
Waiting for the node to be bootstrapped...
Current head: BKxAcD5bE57p (timestamp: 2022-05-19T09:11:50.000-00:00, validation: 2022-05-19T09:12:01.204-00:00)
Node is bootstrapped.
Estimated gas: 1405.942 units (will add 100 for safety)
Estimated storage: 297 bytes added (will add 20 for safety)
Operation successfully injected in the node.
Operation hash is 'ontwST4xWpkc6gR1XaQTZRKDvQLTb1JWEiCLkshhE3YBm5X6TCA'
Waiting for the operation to be included...
Operation found in block: BLfMiFWZNX6ieTVwRVPnLA4j6eThuX4qWGMCTnDBWZjN5YG5jEJ (pass: 3, offset: 1)
This sequence of operations was run:
Manager signed operations:
From: tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez
Fee to the baker: ꜩ0.000421
Expected counter: 10593031
Gas limit: 1506
Storage limit: 317 bytes
Balance updates:
tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez ... -ꜩ0.000421
payload fees(the block proposer) ....... +ꜩ0.000421
Origination:
From: tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez
Credit: ꜩ0
Script:
{ parameter unit ;
storage unit ;
code { DROP ; UNIT ; NIL operation ; PAIR } }
Initial storage: Unit
No delegate for this contract
This origination was successfully applied
Originated contracts:
KT1V1nsrRK9KkvJ3AAXLsnkyzDoG3xtDkyh8
Storage size: 40 bytes
Paid storage size diff: 40 bytes
Consumed gas: 1405.942
Balance updates:
tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez ... -ꜩ0.01
storage fees ........................... +ꜩ0.01
tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez ... -ꜩ0.06425
storage fees ........................... +ꜩ0.06425
New contract KT1V1nsrRK9KkvJ3AAXLsnkyzDoG3xtDkyh8 originated.
The operation has only been included 0 blocks ago.
We recommend to wait more.
Use command
tezos-client wait for ontwST4xWpkc6gR1XaQTZRKDvQLTb1JWEiCLkshhE3YBm5X6TCA to be included --confirmations 1 --branch BMBRRPopGFvHxVQydVxTYYfwSiMK2sPkKjrZbPrqY57nsQSWVTY
and/or an external block explorer.
Contract memorized as simplest.
いろいろ費用を払って、最終的に KT1V1nsrR...
というコントラクトアドレスでデプロイできました。(あなたが実際に貰うアドレスはもちろん違います。) このアドレスは tezos-client
ウォレットの中で simplest
という別名がつけられます。
呼び出してみる
コントラクトの呼び出しは、そのコントラクトに送金することで行われます。送金は、なんだっけ… ./tezos-client transfer ..
でした。送金量は 0
でいいです。
$ ./tezos-client transfer 0 from myself to simplest
Waiting for the node to be bootstrapped...
Current head: BKpsALuxDbY4 (timestamp: 2022-05-19T09:13:55.000-00:00, validation: 2022-05-19T09:14:00.492-00:00)
Node is bootstrapped.
Estimated gas: 2050.478 units (will add 100 for safety)
Estimated storage: no bytes added
Operation successfully injected in the node.
Operation hash is 'ooTxLiR5YWqsh6EKUrLybftg8J6PBd2763ke7iLzrkGSWVat5XC'
Waiting for the operation to be included...
Operation found in block: BLXDfnsZW5UHQqudkNcS6WKwrmMEAEap5WaCk3JdeRHCkgiuua6 (pass: 3, offset: 1)
This sequence of operations was run:
Manager signed operations:
From: tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez
Fee to the baker: ꜩ0.000467
Expected counter: 10593032
Gas limit: 2151
Storage limit: 0 bytes
Balance updates:
tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez ... -ꜩ0.000467
payload fees(the block proposer) ....... +ꜩ0.000467
Transaction:
Amount: ꜩ0
From: tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez
To: KT1V1nsrRK9KkvJ3AAXLsnkyzDoG3xtDkyh8
This transaction was successfully applied
Updated storage: Unit
Storage size: 40 bytes
Consumed gas: 2050.478
The operation has only been included 0 blocks ago.
We recommend to wait more.
Use command
tezos-client wait for ooTxLiR5YWqsh6EKUrLybftg8J6PBd2763ke7iLzrkGSWVat5XC to be included --confirmations 1 --branch BL6DX3qTQALh4xsE3hyE5Pt2Ujsz3Excbo4mk7jjXY2qJHWaEKu
and/or an external block explorer.
手数料を ꜩ0.000467
払って、コントラクト実行した結果は:
Transaction:
Amount: ꜩ0
From: tz1Uuf4NLeeEWcX7p7cDCJhPztyoqrdTTuez
To: KT1V1nsrRK9KkvJ3AAXLsnkyzDoG3xtDkyh8
This transaction was successfully applied
Updated storage: Unit
..
ストレージが Unit
に更新されました。これは SCaml での ()
のことです。
面白くともなんともない?しょうがないです。何もしないコントラクトだもの。
とりあえずのまとめ
つまらないコントラクトでしたが、Tezos でのコントラクトの作り方を一通り見ることができました:
- SCaml でコントラクト
xxx.ml
を書く。 ./scamlc xxx.ml
でコンパイル、Michelsonコードxxx.tz
を得る。- `./tezos-client originate contract «エイリアス» transferring «初期トークン» from «支払い元» running «Michelsonファイル名» –burn-cap «100くらい» でコントラクトのデプロイ
./tezos-client transfer «送金量» from «送金元» to «コントラクトアドレス»
で呼び出す。
すこしずつ複雑に
これからもうちょっと複雑なコントラクトを書いていきたいと思います。