Hyperledger FabricのRead-Set Validationはどのような事態を防いでいるか
Phantom ReadとかDirty Readとかなんかかっこよくないですか、やっていきましょう
なんの話
- ValidationフェーズでRead-SetがChaincode(仮)実行時と現在時点で変わっていないかチェックしている
- これにより二重支払いのような事態の発生を防ぐことができ、台帳の整合性を守る
内容
トランザクションフローの振り返り
コンセンサスの説明のエントリで説明してきた通り、Hyperledger Fabricの台帳を更新するトランザクションのフローでは、EndorsementフェーズでChaincodeが(仮)実行されてからValidationフェーズで書き込み値がコミットされるまでに間があります。また、ValidationフェーズではChaincodeを再実行して書き込み値を改めて生成しているのではなく、Endorsementフェーズから引き継いできたWrite-Setを台帳にコミットしています。
二重支払いケース
ここで、例えば顧客ごとのアカウントIDがKey、所持している日本円の残高がValueになっているような単純な台帳を考えましょう。開始時点のStateではAliceのアカウントは500円、Bobのアカウントは0円、Charlieのアカウントは0円の残高になっています。ここで「AliceからBobに300円送金」というTxを実行するとして、Chaincodeでは当然、送金元=Aliceのアカウントの残高が送金額=300円よりも大きいことを台帳をクエリして確認してからAliceの残高を200(500-300)円に、Bobの残高を300(0+300)円にそれぞれ更新するような挙動を取るでしょう。
しかし、Chaincodeが実行されるEndorsementフェーズではAliceの残高>Bobへの送金額の条件が満たされていても、ValidationフェーズでAlice⇒Bobの送金Txがコミットされる前に「AliceからCharlieに500円送金し、結果Aliceの残高は0円」という別のTxが台帳(State DB)にコミットされるケースがあります。ということは、何も対策をしていないとAliceは残高を二重に使用してCharlieとBob双方に支払いを行えてしまうことになります(二重支払い)。二重支払いが起こると、Alice、Bob、Charlieの残高はそれぞれ200円、300円、500円となり、3者間での送金を行っていただけのはずが、総額が増えてしまいます(台帳の整合性が崩れた状態になる)。
二重支払いの防止(Non-repeatable Read対策)
こうした事態を防ぐために、EndorsementフェーズでのChaincode実行時にState DBからクエリで読み取られたKeyとそのバージョン(これまでの更新回数)のセットから成るRead-Setについて、Validationフェーズでのコミット前の時点でKeyのバージョンが変わっていないかをチェックしています。これにより、上記の例のようなEndorsementフェーズとValidationフェーズの間に他のTxのコミットが入ることによる台帳の整合性が崩れる事態を防止できます。
このRead-SetのValidationにより検知されているのは、Non-repeatable Read Anomalyだと言えると思います(たぶん)*1。
Phantom Read対策も必要
しかし、上記のNon-repeatable Read対策だけでは意図しないトランザクション結果が書き込まれる事態を防ぐのに不十分なところがあります。Phantom Readの対策も併せて必要で、これを素のHyperledger Fabricはやっていたりやっていなかったりします、という話をしたくてこのエントリを書き出したんですがなんかここまでで長くなってるしそのへんは次のエントリで
*1:Non-repeatable Read、およびPhantom Readについてはwikipediaとかでうっすらお勉強しただけなので理解が間違っているかも:https://en.wikipedia.org/wiki/Isolation_(database_systems)#Read_phenomena