空谷に吼える

ブロックチェーン/DLTまわりのなにかしらを書いていく所存

Hyperledger Fabricのコンセンサスを親切に説明 ~ Validationフェーズ編

~~これまでのあらすじ~~前の前のエントリでHLFのコンセンサスのちょう概要がわかったあなたはさらに、前のエントリでEndorsementフェーズで何やってるかも詳細にわかりましたね?
引き続きValidationフェーズのお勉強をし、「Hyperledger Fabric完全に理解した!」になりましょう

なんの話

  • Hyperledger Fabricがどのようにコンセンサスを形成しているかを解説
  • Validationフェーズのプロセスを説明

内容

Endorsementフェーズでやったこと

Endorsementフェーズでどんなんなってたかをかんたんに復習しましょう

  • クライアントアプリケーションがTxのProposalを発行し、Endorserの役割を持ったPeerに送信
  • 各EndorserではChaincodeを仮実行。RWSetとEndorsementを含むProposalResponseを返却:
    • RWSet:Chaincode内で読み取ったKeyおよびそのバージョンから成るRead-Setと、そのChaincodeで書き込む予定のKey-ValueセットであるWrite-Setを合わせてRWSetと呼ぶ
    • Endorsement:当該Endorserの秘密鍵から生成されるRWSetなどの要素への署名を含む。このため後続のプロセスでRWSetは改ざんできない
  • クライアントアプリケーションはEndorsement Policyを満たすだけのProposalResponseが集まった段階でOrdering ServiceにTxを送信し、Orderingフェーズに進むことができる

2. Orderingフェーズ

ここではクライアントからTxを受け取ったOrdering Serviceが、その前後で受け取った当該チャネルでのTx(あれば)とともに1~nのTxを固めてブロックを生成します。このブロック生成時点でTxの順序が確定します。そして生成されたブロックは当該チャネルのすべてのPeerに配布されます。

Ordering Serviceには実はOrdering Service内で改ざんされたらどうするんだ問題、複数Ordererの中でどのように負荷分散するんだっけ問題、停止しているOrdererノードがあったらどうするんだっけ問題などがありますが、Peer間のコンセンサスの位相とちょっとずれるのでここでは扱いません。受け取ったものを素直に固めて配布するものと思ってください。

3. Validationフェーズ

3-1. Transaction Validation

ブロックを受け取った各Peerは、ブロックに含まれるTxに対して順番に以下のチェックを行います。チェックをすべてクリアしていればそのTxはValidと判定され、引っかかればInvalidと判定されます。このValid、Invalidの判定結果はブロックのメタデータ部に保存されます。

  • TxがEndorsement Policyを満たしているか…Endorsementが足りているか、(電子署名に保護されている)RWSetが同一か、など
  • Read-Setに含まれるKeyのバージョンが自身のState DBのそれと一致しているか…Chaincode実行時点で読み取られたKeyが、ここまでに更新されていないことをチェック
    (Non-Repeatable Readにより、意図しない更新が行われてしまうことへの対策。典型的な例としては残高を二重に利用して送金する=二重支払いを防止できる)

3-2. Commit

ブロック内のすべてのTxのValidationを終えると、State DBへの書き込みがコミットされます。この際、コミットされる値は、Validと判定されたTxのWrite-Set(をブロックに含まれる順に処理していった値)ということになります。ここでポイントは、Validation/CommitフェーズではChaincodeを再実行しているわけではなく、Endorsementフェーズで作成されたWrite-Setを書き込んでいる、ということです。ここはテストに出る

ここまででやっとコンセンサスを取りつつTxを台帳に反映させることができました、おつかれさまです

いくつかの補足

以下にちょっとだけ補足事項を記載しています。その他、Orderingフェーズ以外にもいくつかシンプルに説明するために省略していますが重要な事項があるので、そのうちに別のエントリで紹介します

参照トランザクション(台帳の更新を伴わないトランザクション

参照トランザクションの場合、クライアントは任意のEndorserにTx Proposalを投げることで参照結果が含まれているProposal Responseを取得します。複数EndorserにProposalを投げてもいいですが、通常であればどのPeerでも参照結果の値は一致するので、ひとつだけのEndorserに投げれば十分な場合が多いはずです。

参照トランザクションが実行されたことを台帳(のブロックチェーン)に記録したい場合は更新トランザクションと同様にValidation、Commitまで進めればおk。ただしこれはクライアントアプリケーション側の振る舞いに任されているので、常に参照があったことを確実に記録することはできません。

State DBを直接更新してはイケナイ

素のHLFでは、特にState DBとしてCouchDBを使用している場合、わりとかんたんにChaincodeを経由せずにState DBを更新できます(CouchDBのインターフェースがユーザーに露出している)。が、ここまで読んできたひとにはわかっているでしょう、State DBを直接更新してはいけません。Proposal ResponseのRWSetが他のPeerと食い違う、あるPeerでだけValidationが失敗するなどのいやな事態が発生します。

HLFでは、一部のPeerが故障などによってコンセンサスから一時的に脱落するような事態からはそこそこリーズナブルな時間と手間で復旧可能です。が、上記のState DBの改ざんを含め、台帳がforkしている状態になった場合、「不整合が生じていることはわかるんだけどじゃあどれが正しいんだっけ」問題が生じてかなり面倒なことになります。 PoWなどのコンセンサスアルゴリズムではforkが生じた際(PoWではわりと頻繁に生じる)にどれが正(canonical)なのかを決めていくプロセスまで織り込まれていますが、それはそれでfinalityが発生しなかったりとか良し悪しな部分があります。なにごとも良し悪しですね、HLFではforkが起きないようにやっていきましょう