SCamlによるTezosプログラミング#4

Tezos ブロックチェーン のためのスマートコントラクト記述言語 SCaml、チュートリアルの第4回目です。

今回はTezosスマートコントラクトのプログラムを書くにあたって押さえておくべきデータ型についてです。いつになったら SCaml を書き始められるのか…

ほんで Michelson って誰よ

https://upload.wikimedia.org/wikipedia/commons/9/9e/Albert_Abraham_Michelson2.jpg

アルバート・エイブラハム・マイケルソン[..] 光学に関する研究によってノーベル物理学賞を受賞した。[..]エドワード・モーリーと共に有名なマイケルソン・モーリーの実験を行い、エーテルの存在を事実上否定することになった。Wikipedia

Tezos コントラクトの情報

Tezos はアカウントベースのブロックチェーンで、アカウントを作ったら通常はそれを引き回して使っていきます。 アカウントとスマートコントラクトの違いは簡単にはコントラクトのコードがあるか、ないかです。 一般的には両者まとめてコントラクトと考えればいいでしょう。

Tezos のコントラクトにはいろいろな情報がありますが、スマートコントラクトプログラミングにに関係するものだけ挙げると:

アドレス (address 型)
tz1..., tz2..., tz3..., KT1... などのアカウント、コントラクトを識別する文字列です。

tz... から始まるものはコントラクトのないアカウントのアドレス、

KT1... はスマートコントラクトのアドレスです。KT は KonTract の略。

公開鍵 (key 型)
edpk... から始まります。pk は “Public Key” の略。

公開鍵署名の検証に用いられる公開鍵の型です。各アカウントにはそれを作成した秘密鍵と公開鍵のペアが紐づいています。

アカウントの秘密鍵はアカウントの所有者しか知りません。(他人に教えないようにネ!)

アカウントの公開鍵はアカウントが作成された段階ではブロックチェーン上には公表されていません。 アカウントの所有者が reveal という操作を行って初めて、アカウントに紐づいた公開鍵が公表されます。 この reveal という段階があるので、公開鍵さえわからないアカウントを作ってそこに資産を移し、より安全に 管理することが可能になっています。

残高
どれだけトークンが残っているか
コントラクトコード
Michelsonコード。 KT1... のアドレスを持つコントラクトはスマートコントラクトコードを一つ持っています。 このスマートコントラクトコードは紐づいたコントラクトに送金(額は0でもよい)を行うことで起動されます。

tz... から始まるアドレスはスマートコントラクトコントラクトコードを持てません。

ストレージ
スマートコントラクトを持つ KT1... コントラクトは残高以外にも記憶域 (storage) を持っています。スマートコントラクトコードはこの storage を変更していくことで状態管理を行います。

そして最後に、

秘密鍵
edsk... から始まります。 sk は “Secret Key” の略。

ブロックチェーンには現れません。ウォレットは知っています。この実習では $HOME/.tezos-handson-client/secret_keys に記録されています。

Operation

Tezos のコントラクトの状態変更を operation と言って、スマートコントラクトが発生させることのできる operation には次の三つがあります:

送金(transaction/transfer tokens)
手持ちのトークン(0でもよい)を指定したアカウントに送ります。送り先のアカウントがスマートコントラクトの場合、そのスマートコントラクトコードが実行されます。次の情報を持っています:
  • トークン量
  • 送付先コントラクト
  • コントラクト引数: スマートコントラクトを実行する際に使う引数です
起債(origination/create contract)
指定したプログラムコードを持つアカウントを作成します。次の情報を持っています:
  • コード
  • デリゲートアドレス(オプション) (気にしなくていいです)
  • トークン量
  • 初期ストレージ

Origination が生成するコントラクトは常にコントラクトコードを持つKT1..コントラクトです。 tz1..などのアカウントはスマートコントラクトからは生成できません。

委託先変更(set delegation)
PoSのデリゲーション先を変更する。今回は触れない:
  • 委託先キーハッシュ/なし

Tezos のスマートコントラクトコード

Tezos のスマートコントラクトは副作用のない関数になっています:

入力引数
  • 実行時に与えられた引数
  • 実行時にコントラクトに保存されているストレージの値

上記の関数への入力以外にも、送金量や実行時刻などの情報も取得可能。

関数の戻り値
  • 実行したい operation のリスト
  • 新しいストレージの値

副作用が無いので、コード実行中に動的にストレージの値を変更することはできません。 ストレージを変更したい場合は、関数実行の最後に新しいストレージの値を関数の戻り値として返してやると、 その関数の実行が終了した後に、値が書き換えられます。

スマートコントラクト実行モデル

この実行モデルをもう少し詳しくみてみましょう:

  • スマートコントラクトが実行されると、新しいストレージと operations を得る。
  • 実行されたスマートコントラクトのストレージは新しいものに置き換わる
  • 得られた operations を実行する。
  • Operations の中の transaction の送り先がスマートコントラクトであれば、そのコードを実行する。

これを未処理の operation が無くなるまで続けます。

これを絵にするとこんな感じになります:

https://gitlab.com/dailambda/images/-/raw/master/tezos-call-model.svg
  1. コントラクトAのコントラクト関数を実行すると、オペレーションと新ストレージが返ってきます。
  2. コントラクトAのストレージは新ストレージの値に更新されます。
  3. オペレーション中にコントラクトBの呼び出しがあった場合は、その呼び出しを呼び出しキューに追加します。
  4. 呼び出しキューの先頭にコントラクトBの呼び出しがあれば、そのコントラクト関数を実行します。 関数の実行は別のオペレーションとコントラクトBの新ストレージを返します。 コントラクトBのストレージを更新して、他のコントラクトの呼び出しがオペレーションにあればキューに追加します。
  5. これをキューが空になるまで続けます。

うーん、わりと複雑ですね。でもこれは reentrancy bug が発生しにくくするためのデザインです。単純な関数呼び出しモデルでは reentrancy bug がすぐに発生してしまうのは第二回で解説しましたのでご覧ください。

エラー処理: All or nothing

コントラクトの実行が新しいコントラクトの実行を生み出し、それが連鎖していく実行モデルですが、どこか途中で実行にエラーが発生したらどうなるのでしょうか。

Tezos ではエラー時の挙動は非常に簡単です。どこかで実行がエラーで失敗した場合、関係する全てのスマートコントラクトの実行がキャンセルされます。それまでに送られたトークンや変更されたストレージは全部キャンセルされます。ストレージが中途半端に更新される、といったことは起きません。安全です。