本記事は、2018年3月8日から10日まで行われたEthereum Community Conference(EthCC)で発表されたPlasma Cashについて解説します。
Plasma Cashの前提知識であるPlasmaと、そもそもPlasmaが何を解決しようとしているのかという点について解説し、Ethereumのスケーラビリティ問題への理解を深めることを目的とします。
前提知識としてブロックチェーンの構造、Ethereumについての知識があることが望ましいです。
ブロックチェーンのスケーラビリティ問題
まず、ブロックチェーンにおけるスケーラビリティの問題とは具体的にどのようなものを指すのか、具体的な例を通して見ていきましょう。
例えばEthereumでは、人気のInitial Coin Offering(ICO)により、一時的にトランザクションが大量に発生し、トランザクションが長時間承認されないという事態が発生しています。
また、2017年末ごろ、子猫を育成するアプリケーションであるCryptoKittiesが人気になった時も、一時的にEthereumのネットワークが混雑しCryptoKittiesを利用していないユーザーのトランザクションにも影響が出たことがありました。
このように、イーサリアムのチェーン上で処理できるトランザクションの限界を一時的に超えてしまい、送金詰まり、トランザクション詰まりと呼ばれる現象を引き起こすことがあります。
ことEthereumはアプリケーションプラットフォームとしての役割から、イーサリウムのチェーン上であらゆるアプリケーションを稼働させるために、スケーラビリティの向上が解決すべき必須の問題となっています。
Plasmaとは?概要と仕組み
Plasmaはブロックチェーン本体と接続する形でもうひとつ別のブロックチェーンを接続し、処理を後者のチェーンに移譲することによって処理性能を向上する技術です。このような手法は一般的にサイドチェーンと呼ばれます。
μRaidenとは異なり、こちらはブロックチェーンに接続しているので、通常のスマートコントラクトのトランザクションも実行可能ですし、特定の処理に特化したPlasmaチェーンを用意することもできます。Plasmaチェーンを構築する主体は大元のEthereumチェーン上のコントラクトに資金をデポジットしておき、Proof of StakeまたはProof of Authorityで各々のトランザクションを処理します。
また、サイドチェーンの下にさらなるサイドチェーンを接続することも可能で、下図のように階層的にブロックチェーンを用意し処理を移譲していくことで2段階目、3段階目の処理性能向上を行うことが可能です。
CryptoKittiesのように、大量のトランザクションが発生するアプリケーションの処理が問題になった場合は、アプリケーション固有のPlasmaチェーンを用意するなどということも可能です。
一番下のブロックチェーンから2,3段階目のPlasmaチェーンに処理をしていきます。
Map Reduce処理
PlasmaはMap Reduce処理を用いて処理速度を大幅にアップさせることができます。
ここで言うMap処理とは、大元のブロックチェーンに存在する大きな処理のトランザクションを子階層のPlasmaチェーンに分割する処理を指します。分割したチェーンの下に、その子Plasmaチェーンが存在する場合はさらに分割します。
こうやって分割した処理は計算量が減るため、各々の処理は高速に完了します。
計算が完了したあとは、親階層のチェーンに処理結果をまとめあげて送ります。この処理結果をまとめ上げる処理がReduce処理と呼ばれるものになります。
(HaskellやScalaなどの関数型言語の経験がある方であれば、リスト操作の際によく使うmap()やreduce()をイメージしていただければお分かりになるかと思います)
親階層のチェーンでは、子チェーンから送られてきた処理結果をさらに結合し、さらに親階層のチェーンに送ります。
この処理を大元のブロックチェーンまで続けることで大量のトランザクション処理を行うことが可能となります。
①Step1の処理(図の青文字部分)でトランザクション処理を子Plasmaチェーンに分割します。
②Step2で子Plasmaチェーンで完了した処理を親チェーンに送り、まとめあげます。
これを大元のチェーン(図では緑色)まで繰り返します。
多段階層チェーン
これらのMap Reduceの処理は多段にすることができます。
上記の例ですとPlasmaが3階層になっていますが、これは何階層でも実現可能です。階層を多くすればするほど分割するステップが増えますが、どれだけ分割して階層化しても大元のEthereumのチェーンに書き込む回数は変わりません。
また、分割する粒度を細かくし、水平方向にPlasmaチェーンを増やすことで、1つのチェーンで処理する内容を削減することができます。
この場合も分割のコストは増加しますが、大元のチェーンに書き込む回数に変化はありません。このためスケーラビリティには原理的に限界はないといえます。
(実際には、分割コストやネットワークのコストがあるため、どこかで性能が頭打ちになると考えられます。)
Fraud ProofによるBlock withholding attacksの回避
Plasmaのチェーンの構築運用は不特定多数に任されています。
そのため、もしも利用しているPlasmaチェーンが悪意ある者によって運営されていたとすると、最悪の場合、Plasmaチェーン上で利用している資金が失われてしまうことが考えられます。
そうした不正を防ぐために、ユーザーアカウントは、いつでも自分の資金が不正に利用されていないか確認する必要があります。
そして、もし不正が行われていた場合は親チェーンに対して不正が行われた証明(Fraud Proof)を出すことができます。
このFraud Proofを出すことで、提出から一定の時間が経過した後に、ユーザーアカウントは不正が行われる以前の状態の資金を引き出すことができます。また、このような不正を行ったブロックの生成者はペナルティを受けることとなります。
Plasma MVP
Plasma MVPはPlasmaの最小限の簡易実装として提案されており、OmiseGOプロジェクトなどで利用される予定です。
GitHub上に公開されており、Consensysが提供しているGanacheを利用することで簡単に挙動を確認できるようになっています。
Ganacheをインストールし、Ganache上のプライベートチェーンにPlasma用のコントラクトをデプロイしたものをこれをルートチェーンと呼びます。
そしてルートチェーンにひもづく形でPlasmaチェーンを起動したものをチャイルドチェーンと呼びます。
具体的なサンプルの動作方法はGitHubにまとめられているので興味があれば動かしてみてください。また、具体的な動作の流れはKarl Floersch氏がYouTubeで公開しています。
Plasmaの課題
ユーザー自身によるチェーン監視が必要
Ethereumの処理性能を向上させるスケール技術としては有力であるPlasmaですが、ユーザーが前述の不正証明を行うためには、自分が発行したトランザクションが関わるチェーンを全て監視しておく必要があります。
監視できていなければ、どこで不正が行われたのか検証が不可能だからです。
多段階層化によるチェーンサイズの肥大
また、それぞれのチェーンのトランザクションを確認する必要があるため、関わるブロックをダウンロードする必要性が出てきます。
チェーンが多段構造になればなるほど、ユーザーがダウンロードするブロックのサイズは増えていき、利便性を損なうことになります。
アカウントの状態管理が煩雑になる
もう1つの課題はPlasmaチェーン上で処理されたトランザクションの状態を更新するためにはアカウントの状態をアップデートする必要があるということです。
Plasmaチェーンでの処理内容が大元のチェーンに取り込まれた後、トランザクションを発行したユーザーは、トランザクション対象のアカウントに正しく処理が完了したことを通知する必要があります。
これはUTXOモデルとアカウントモデルを併用した欠点と言えますが、単純にアカウント更新の二度手間となってしまいます。
Plasmaの課題を解決するための提案”Plasma Cash”
Plasma Cashは、Plasmaが抱えるこれらの問題を解決しようとする試みです。
この章からは、Plasma CashがどのようにしてPlasmaの課題に取り組んでいるのかを見ていきましょう。
まずはPlasma Cashがどのようなコンセプトに基づくものか、その概要を説明します。
デポジットされたトークンに固有のIDを発行する
PlasmaおよびPlasma CashはUTXOモデルを採用しており、トークンIDを振ることによりトランザクションを全て固有のものと捉えることができます。
(ここでメインチェーンの例として挙げているEthereumでは、UTXOではなくアカウントモデルが採用されていることに注意してください)
Plasma Cashでは、Plasmaチェーン上にデポジットされた一つ一つのトークンに、それぞれを識別できるようにユニークな識別子(ID)を付与します。
例えばポケットの中にある現金を数える際には、1枚目の千円札、2枚目の千円札、、、というようにそれぞれに頭の中で番号を振りながら数えるかと思います。その場合と同様、Plasma上のトークンにユニークな番号を振ろうというコンセプトです。
これがPlasma Cashと呼ばれる所以ですが、この番号を振るという行為で多くのメリットがもたらされ、Plasmaの課題を解決することができます。
以下Plasma Cashにおいて、plasmaのチェーン上にデポジットされたトークンに振られるユニークな番号のことを「トークンID」と呼びます。
ダウンロードすべきチェーンサイズの軽量化
まずはチェーンサイズの肥大化問題を解決するために、Plasma Cashがトランザクションをどのように管理しているのかを見ていきます。
トランザクションの構造
Plasma Cashチェーンにあるすべてのブロックのルートは、そのルートチェーン上(Ethereumのメインチェーンなど)に公開される必要があります。
ここで用いられるマークル木の中では、トークンIDと各ノードのインデックスが対応するように、順番に整理されています。
トークンが使用されていない状態では、各ノードの値は空となっています。
トークンが使用され、そのトランザクションが正常に処理されると、使用されたトークンIDに対応した位置(インデックス)のノードにトランザクションのデータが保持されます。
トークンIDがを紐づけられたトークンを消費するトランザクションは、それがマークル木のトークンIDの位置に含まれている場合にのみ有効となります。
たとえば、トークンが使用されていないことを示す(二重使用を防ぐ)際には、トークンIDと1対1で関連づけられているインデックスのノードが空であることを示せば良い、ということになります。
Plasmaではユーザーが自分が関わっているトランザクションを監視するために、全ての子階層のPlasmaチェーンをダウンロードする必要がありました。
しかしPlasma Cashでは、トークンIDに対応したノードのみを提示すれば済むので、取得すべきデータ量が削減されます。
そのため、クライアント側の検証作業が簡略化され、トランザクションのスループットを向上させることができます。
なお、トランザクションの形式は次のようになっています。
[[prev_hash、prev_block、(target_block?)、token_id、new_owner]、signature]
アカウント状態の更新処理の削減
次に、Plasma Cashチェーン上にトークンをデポジットする手順について見ていきます。
Plasma Cashのチェーンにトークンをデポジットする
Plasma Cashのチェーン上にトークンをデポジットするには、メインチェーンで公開されているPlasmaのコントラクトにあるdeposit()関数を呼び出します。
デポジット関数は、呼び出された際に一緒に送られた量のトークンを元に新しいトークンを生成します。
ERC20トークンの場合はdepositToken(address erc20, uint256 denomination)を呼び出します。
この関数は、ERC20コントラクトに対してユーザーが所有するトークンを引き出し、トークンタイプ(ERC20コントラクトのアドレス)とデポジットするトークンの量を記録します。
Plasma Cashの利用
Plasmaチェーンでトークンを利用するユーザーは、利用したい特定のトークンの完全な履歴を送金するユーザーに提供する必要があります。
Plasmaチェーン上にデポジットされたトークンを消費するトランザクションが発行されるたびに、マークル木による証明がなされます。
対応するトークンIDの箇所をマークル木からブランチとして取得し、その箇所にトランザクションが入っていれば、それはトークンが利用されたこととなります。
逆にデポジットされたトークンが消費されなかった場合には、対応するトークンIDの箇所にトランザクションが入っていないことを確認します。
次に、送信されたトークンを受け取ったユーザーは、トークンの履歴をチェックします。
正しいトークンIDの箇所にトランザクションが入っているかを確認し、問題がなければ、受信者はトークンを使用できるようになります。
この一連のトークン送信の処理で、トークンを受信したユーザーのみが送られたトークンを利用することができます。
PlasmaおよびPlasma CashはUTXOモデルを採用しているため、トークンIDを振ることによりトランザクションを全て固有のものと捉えることができます。
これにより、Plasmaチェーン上で処理されたトランザクションの状態を更新するために、都度親チェーンのアカウントの状態をアップデートすることなく所有者の移転が可能となります。
不正なイグジット(離脱)の通報
イグジット(Exit)とは、Plasmaのチェーンから、親であるイーサリアムのメインチェーンにトークンを戻す行為です。
しかし、このイグジットを悪用して、例えばPlasmaのチェーン上で5Etherしかもっていないのに、それ以上の額(10Etherとか)をメインチェーンに戻そうとするなどの不正行為が行われることがあります。
その対策として、トークンのデータに携わっているすべてのユーザー(ノード)は不正申告を行うことができます。
各イグジットは、申告を行ったユーザーのgasコストと不正に対する賞金を含んでおり、不正を告発したユーザーはこの賞金を受け取ることができます。
イグジットに対する不正申告の種類には次の3つのタイプがあります。
1) イグジットするユーザーは3を用いてイグジットしようと試みます。
2)不正申告ユーザーは3への参照を持つ4を用いてイグジットの不正を申告します。
イグジットユーザーは4から2への参照を用いて4をイグジットしようと試みます。
不正申告ユーザーは2への参照がある3を用いてイグジットの不正を申告します。
1) イグジットするユーザーは5と4を用いて5をイグジットしようと試みます。
2) 申告者は最後の有効な2と1を用いて不正申告をします。
3) イグジットするユーザーは有効な2の次の有効なトランザクションを答える必要があり、存在しない場合にはイグジットがキャンセルされます。
これは不正なブロックが保留されている場合でも動作します。
Plasma Cashの課題
Plasma Cashにより、Plasmaの課題を幾分解決することができましたが、さらに改善する余地は残っています。
下記にいくつか、Plasma Cashの主な課題を紹介しておきます。
部分的な支払い
Plasma Cashでは、トークンIDは分割不可能なため、トークンを分割して支払うことができません。
これに対する簡単な解決策としては、トークンIDが小数点をサポートすることが考えられます。
Plasmaで完結するための理想的な手法としては、Plasma上でトークンを分割し、分割されたものを一つのトークンにまとめあげてイグジットする手法がありますが、仕様がまだ決定していません。
効率的なイグジット
イグジットの処理を簡潔にすることが求められます。
無効なトランザクションの後は、すべてのサブトランザクションが無効であると仮定します。
そうすると、無効なトランザクションが含まれる直前にイグジットできるユーザーは、そのトークンの持ち主である、と言うことができます。
実際にはトランザクションの検証をしているので、無効なトランザクションがあったとしても問題にはならないのですが、イグジットのロジックが複雑になってしまいます。
Block withholding
Block withholdingの説明ですが、まずノードの中で、とあるブロックがブロードキャストされていない状態を想像してください。
そのブロックのトランザクションを考慮しない状態では、一連のトランザクション群は正常であるといえるのですが、その隠されたブロックがブロードキャストされ、そのトランザクションを考慮すると、二重消費などの不正が起きてしまう問題です。
この問題に対処する方法が必要とされます。
また、blocktime以上にblockがholdされた際、それが悪意を持った攻撃なのか、何らかのミスやトラブル、単なる遅延なのかを判断するのが難しく、規定時間を過ぎたブロックを単純にスキップしてもいいのか?という部分の判断もさらなる議論が必要です。
最後に
今回解説したPlasmaは、イーサリアムのスケーリングを解決する手法として今後とも多く注目を集めていくであろう技術です。
とはいえPlasma Cashはまだ実装がなく、今後技術的な変更がなされる可能性があります。
Plasma Cashに関しては、Ethereum Researchなどの場で日々議論がなされています。興味を持った方はそちらもウォッチしてみてください。
参考資料
https://karl.tech/plasma-cash-simple-spec/
https://ethresear.ch/t/plasma-cash-plasma-with-much-less-per-user-data-checking/1298
https://zoom-blc.com/what-is-plasma-cash