Tezosの内部挙動を理解する - ブロック検証編

概要

Tezosも含めたBitcoinやEthereumなどのブロックチェーンにおいては、口座間の送金やスマートコントラクト呼び出しなどの現在のブロックチェーンの状態に対する差分操作がブロックとして表現されます。

この記事では、Tezosのプロトコル(バージョン 010 Granada)において、どのようにしてブロック検証が行われているかを、Tezosのコードを追うことによって解説していきます。実装が日々改善されているシェルのコードに関しては、執筆時点で最新の安定バージョンであるv10.2のコードに基づきます。

シェルにおけるブロック検証と生成

Validatorによるブロック検証

Validator(tezos-validator)は、ノード(tezos-node)を起動すると裏で別プロセスとして立ち上がり、ノードに送られてきたブロックに対して、そのブロックが実際に正しいブロックであるかどうかを検証します1。 Tezosにおけるvalidatorはドキュメントにあるように様々な役目があるのですが、このうち特にblock validatorによってブロックの正しさが確かめられています。

ソースコード上においては、validatorバイナリを起動するとbin_validator/Validator.mainが呼び出され、ここからlib_validationapplyが呼び出されます。この関数では、以下の手順でブロックが検証されます。

  1. 現在のブロックチェーン状態(context)上に保存されている現在のプロトコルハッシュから、対応するプロトコルのモジュールを取得
  2. プロトコルのモジュールからBlock_validationモジュールを作り、contextに対してBlock_validation.applyを呼び出し、新しい状態を含むブロック生成結果を返す

この手順中に何かしらエラーが発生したり、得られた新状態のハッシュがブロックが主張する新状態のハッシュと異なる場合は、ブロックは不正であると判断されます。

Block_validation.applyの定義は lib_validationBlock_validation.Make() に存在します。この関数では

  • 現在のcontextに対するoperation列の適用
    • Proto.begin_applicationで現在のcontextから状態stateを生成
    • stateに対してProto.apply_operationを繰り返し各operationについて適用
    • Proto.finalize_blockを用いてstateから新たなcontextやcontextに対する操作内容であるブロックを生成
  • (必要なら)新プロトコル/新protocol environmentへの切り替えを行う

などが行われています。

Protoモジュール以下の関数は現在のプロトコル固有の関数であり、具体的なブロック生成検証は各プロトコルによって異なります。

Bakeによるブロック生成

tezos-clientで起動できるサブコマンド2の中に、

Commands related to delegate operations.:
  bake for <baker> [オプション省略]
    Forge and inject block using the delegate rights.
    <baker>: name of the delegate owning the baking right

があります。このサブコマンドは、コード上では各プロトコルにおけるlib_delegate/delegate_commands.ml中のdelegate_commandsにおいて定義され、lib_delegate/delegate_commands_registeration.ml中でClient_commands.registerを呼び出すことによってtezos-clientのコマンドとして登録されています。

このコマンドにおけるoperationの扱いを追って行くと、lib_delegate/client_baking_forge.ml中のfilter_and_apply_operationsにたどり着きます。この関数の中で、前セクションで紹介したBlock_validation.applyと同様に、Client_baking_simulator.add_operationの各operationについての繰り返し適用が行われています。add_operationは内部的にProto.apply_operationを呼び出しており、これより内部の処理はvalidatorにおける処理と共通になっています。

Bakerは選んだoperationを適用し、新状態を得、その情報(ハッシュ等)を使ってブロックを生成します。

プロトコルにおけるoperation適用

以降では、特にGranadaプロトコルにおいて、apply_operationの内部の処理を追っていきます。この関数では、種々のoperation(命令)の適用が行われます。各operationを適用した結果の残高の遷移は、関数の返すreceiptの内容からわかります。

各operationは、tezos-clienttezos-bakertezos-accuserなどを用いて生成されます。たとえば./tezos-client transferコマンドを叩くことでTransaction operationを生成することができます。 Operationの中には、スマートコントラクト実行中にMichelson命令によって生成されるものがあります。たとえば、Transaction operationはMichelsonのTRANSFER_TOKENSが実行されることでも生成されます。

Operationに関しては、既存の記事 The Lifecycle of an Operation in Tezos でも説明されています。この記事の公開日時は2019年5月と昔ではありますが、この記事で紹介されている11個のoperationは以下で紹介するGranadaにおけるoperationと対応しています。

Tezosでは送金など、一般の tz* アカウントが行える操作のことを “manager operation” と呼び、内部的にはmanagerではないoperationとは別の関数で処理を行います。Manager以外の operation はブロックの承認やオンチェーン・ガバナンス投票関連など、roll を持つ baker が発行する命令です。

Manager operation

Manager operationは Apply.apply_manager_operation_contentにおいて処理されます。

Managerであるoperationは以下の4種類です。すべてlib_protocol/operation_repr.mlで定義されています。

  • Transaction {amount; parameters; destination; entrypoint}
    • コントラクト間の送金を行う命令です。amountは送金額、destinationは送金先アドレスです。受け取り側のコントラクトがスマートコントラクト(originated account)である場合、entrypointに従ったエントリポイントに対して、parametersを引数としたスマートコントラクト呼び出しが行われます。
    • tezos-client transferコマンドや、MichelsonのTRANSFER_TOKENS命令を使うことで生成されます。
  • Origination {delegate; script; preorigination; credit}
    • 新しくスマートコントラクトを作る命令です。scriptをコードとして持つスマートコントラクトアカウントが作られます。
    • MichelsonではCREATE_CONTRACT命令を使うことで生成されます。
  • Reveal pk
    • この命令のsourceアカウントが公開鍵(public key)をまだ公開していなかった場合、そのpublic keyを公開します。スマートコントラクトのmanagerのkey状態には2種類あり、最初はpublic keyのhashだけが分かっている状態(Hash)ですが、この命令を用いることで具体的な公開鍵が分かっている状態(Public_key)になります。
    • アカウントが発行した operation はそのアカウントの秘密鍵によって電子署名されます。第三者がこの operation の正当性を確認するためにはアカウントの公開鍵が必要で、これをブロックチェーンに登録するのがRevealです。
    • tezos-clientを用いてmanager operation列を発行した場合、内部的にはlib_clientinject_manager_operationによってtezos-nodeへの送信が行われます。この際、もし送信元が公開鍵をrevealしていなかった場合、自動的にReveal命令がmanager operation列の先頭に付け加えられます。なお、tezos-client reveal key for .. を使って明示的に生成することもできます。
    • このmanager operationを明示的に生成する Michelson の命令はありません。
  • Delegation delegate
    • 口座残高から生じるbaking権やendorsing権をdelegate(移譲)するための命令です。delegateはoption型であり、Someの場合はdelegateの設定を、Noneの場合はdelegateの解除を行います。
    • MichelsonのSET_DELEGATE命令によって生成されます。

lib_protocol/script_interpreter.mlstep関数内を読むことで、各Michelson命令の実行時にどのようにしてmanager operationが生成されるかが分かります。

Manager ではない operation

Managerではないoperationはapply_contents_list関数において処理されます。

Managerではないoperationは以下の7種類です。manager operationと同様、すべてlib_protocol/operation_repr.mlで定義されています。

最初の2命令(Endorsement_with_slot, Seed_nonce_revelation)は報酬を貰うための命令です。

  • Endorsement_with_slot {endorsement={shell={branch}}; slot}
    • ブロックbranchに対するendorsement(裏書)を行う命令です。branchが直前のブロックであるかやendorsement権slotが未使用であるかなどが確認され、そうである場合、残高からendorsement depositを一時的にfrozen_depositに移し、endorsement報酬がfrozen_rewardsへ追加されます。
    • この報酬は権利slotをもつdelegateに対して入ります。
  • Seed_nonce_revelation {level; nonce}
    • 過去のlevelのnonceを明らかにして報酬をもらう命令です。levelは1つ前のcycleに属している必要があります。
    • この報酬はbakerに対して入ります。

次の2命令(Double_endorsement_evidence, Double_baking_evidence)は二重に報酬を貰おうとする行為を報告し、報酬をもらうための命令です。

  • Double_endorsement_evidence {op1; op2; slot}
    • Double endorsementの証拠を持ってきて報酬をもらう命令です。op1op2がどちらも権利slotによって行われたEndorsement命令3である場合、Delegate.punishによって権利slotを持つdelegateのfrozen_balance(frozen_depositfrozen_feesfrozen_rewards)から残高が没収されます。
    • 報告報酬はbakerに入ります。
  • Double_baking_evidence {bh1; bh2}
    • Double bakingの証拠を持ってきて報酬をもらう命令です。bh1bh2がどちらも同じdelegateによって生成されて、かつ古すぎず新しすぎないlevelの場合、Double_endorsement_evidenceと同様にDelegate.punishによって残高が没収されます。
    • 報告報酬はbakerに入ります。

次の命令(Activate_account)はICOアカウントを有効化するための命令です。テストネットでのfausetアカウントの有効化にも使われます。

  • Activate_account {id; activation_code}
    • ICOアカウントを有効化するための命令です。
    • テストネットの場合、たとえば、https://faucet.tzalpha.net/を用いるとfausetアカウントが得られますが、これをactivate accountコマンドによって有効化すると、内部的にはlib_clientactivate_accountが呼ばれ、この中でActivate_account命令が発行されます。

最後の2命令(Proposals, Ballot)はオンチェーン・ガバナンスのための命令であり、https://tezos.gitlab.io/active/voting.htmlに詳しい説明が記載されています。

  • Proposals {source; period; proposals}
    • sourceから送られてきたプロトコル提案(proposals)を記録する命令です。受信時点でのvoting periodがProposalである必要があり、加えてperiodが現在のvoting periodのindexと一致している必要があります。各delegateが一度に何個のproposalを提案できるかはlib_protocol/constants_repr.mlに記載されており、たとえばGranadaの場合は20個です。
    • Proposal期間に送られてきたproposalsを総計して、一番得票数の多いprotocolが現在の提案protocolになり、次のvoting periodであるExploration期間に移行します。
  • Ballot {source; period; proposal; ballot}
    • sourceから送られてきた投票を記録する命令です。受信時点でのvoting periodがExplorationもしくはPromotionである(投票フェイズである)必要があります。加えて、periodが現在のvoting periodのindexに、proposalが現在の提案proposalのprotocol hashに一致している必要があります。ballottype ballot = Yay | Nay | Passを持ち、それぞれ現在のプロトコルに対する賛成/反対/棄権を表します。

  1. tezos-validatorBlock_validator_process..start_process で起動されます。 ↩︎

  2. どのようなサブコマンドがあるかは、./tezos-client --endpoint https://rpc.tzstats.com manなどで確認できます。 ↩︎

  3. Endorsement命令は、この記事で紹介した11の命令とは別に、Endorsement_with_slot命令やDouble_endorsement_evidence命令に包まれた内部的な命令としてのみ現れる命令です。 ↩︎