汎用的なプラズマの構造を理解しよう
Understanding the Generalized Plasma Architecture
本記事は2019年3月26日にプラズマR&DグループであるPlasma GroupのMediumで公開されたUnderstanding the Generalized Plasma Architectureの記事をCryptoeconomics LabのYuriko Nishijimaが日本語翻訳したものです。
元の記事↓
プラズマの研究の最先端をお伝えするアドバンストな内容となっていますので、プラズマの基礎から抑えたい方は、まずこちらの記事を読んでみてください。
また、後半部分で説明している具体的なプレディケートの仕様は2019年5月31日現在変更されている部分もあるので、Plasma Groupのプレディケートの仕様に関する最新情報はこちらのドキュメントをご覧ください。
私たちは先日、汎用性の高いアプリケーションを開発できるプラズマについての記事を公開しました。今回は、そのアーキテクチャーについてさらに詳細を追っていきたいと思います。まずはじめにプラズマの構成要素を大まかに解説し、Pythonで書かれたサンプル実装を見ていきます。すでにプラズマに詳しい方は図が載っている段落までスクロールしてもらって構いません。
はじめに
前回の記事で話した大事な点
- プラズマのリサーチを全て追うのは大変である。
詳しい人ならおそらくプラズマには色々な派生があることをご存知でしょう。それぞれの派生で良いところ・悪いところのトレードオフがあり、1つ1つの研究の進捗を追い続けているだけで1日が終わってしまいます。
- ほとんどのデベロッパーはスケーラブルなDappsを簡単に作る方法さえ得られれば満足なのである。
みんなが素晴らしいデベロッパーツールやしっかり整備されたライブラリが欲しいと思うのは当たり前で、一つのアプリを作るために一からブロックチェーンを開発したいと思う人は少ないでしょう。
Plasma Groupはプラズマの専門家でなくてもプラズマチェーン上でちゃんとスケールするアプリケーション(Plasma上のApplicationなので名付けて『Plapp』)を簡単に作れるプラットフォームを作りました。『Plapp』という名前はまだ耳馴染みが無いかもしれませんが、ぜひ覚えてくださいね😄
レイヤー2とは:ステートに関する主張
レイヤー2でやりたいことを一言で表すと、『メインチェーン上にあるユーザーの資産(オンチェーンデータ)の保証を提供するために、メインブロックチェーンの外のデータ(オフチェーンデータ)を使う』ということです。
例えば、そのオフチェーンデータはイーサリアムにデプロイ済みのエスクローコントラクトに保持されている資産を引き出す権利をユーザーに与えるかもしれません。しかしこの場合、イーサリアムブロックチェーンを介することなくその資産の所有権が移されているということになります。
しかしながら、オフチェーンデータを使って、結果的にメインブロックチェーンでなにかできなければそれは有用であるとは言えません 。
メインチェーンに新しい動作を起こさせるためには、大体の場合、オフチェーンデータに関する主張をその資産の乗っているメインチェーンのスマートコントラクトに提出する必要があります。例えば『私が資産Xを引き出すことを許可してくれたこの署名つきのメッセージを私は持っています』などというものです。
しかし、もしその主張を提出した人が新たにその資産 X を他の誰かに送るメッセージに署名してしまったとしたらどうでしょうか? これは最初に提出された出金を無効にしてしまいます。 このような2回目の不正な出金が確実に成功しないようにするため、すべての主張に対し反証期間(チャレンジ期間)を設けています。誰でも期間内であればその主張に対して反論することができます。誰からも反論されることがなければその主張は正当であったと認識されます。シンプルでわかりやすいですね!
この設計をプラズマに当てはめてみましょう。プラズマチェーンはブロックの連なりでできていて、一つ一つのブロックはトランザクションの連なりで出来ています。プラズマチェーンブロックが作られるたびに、暗号化されたコミットメントがメインチェーンにパブリッシュされます。これらのコミットメントは何かがそのブロックに含まれていたと証明するために使うことができます。プラズマの場合、このコミットメントはブロックの中の全てのステートアップデートから作られたマークルルートです。
イーサリアムブロックチェーンにデプロイされたプラズマのスマートコントラクトはプラズマブロックがコミットされた順番を記録し、コミットメントがその後書き換えられることを防ぎます。つまりこれらのブロックのコミットメントは私たちに時間の概念を与えてくれるということです。 ユーザーが何かがプラズマチェーンで起こったという主張をしたい時、彼らはそれらがいつ起こったか伝えるためにブロックナンバーを参照しなければなりません。例えばあるユーザーが『これは私にアセット Z をくれたブロックYに含まれているトランザクションXだよ』と伝えたかったとします。その際そのトランザクションがいつ起こったかという時系列に関する付加情報は、何が先に起きて何が後に起きたか把握し不正なトランザクションに対して反証するためにとても重要です。
オフチェーンデータの使い方まとめ
ここまで見てきたようにレイヤー2とは、主になんらかのオフチェーンデータを使ってオンチェーンに乗っている情報に影響を与えるためにあるものです。ここまでの例ではオフチェーンデータは主に何らかの資産の所有権を表すために使われてきました。誰によって所有されたか、そしてその所有権が他の誰かに移っていないかどうか、といった情報です。
例えば、アリスが自分の資産をイーサリアム上のスマートコントラクトにデポジットすると、アリスがその資産の所有者になります。そしてアリスはその所有権をボブに移すオフチェーンメッセージに署名することもできます。その後ボブはメインチェーンに対して署名されたそのメッセージを使って資産の所有権は自分にあると主張し、その資産をイーサリアムメインチェーンに引き出すことができます。
また、資産の所有権の移転以上に複雑なことも表現することができます。たとえばアリスがクリプトキティをメインチェーンにデポジットし、次々とオフチェーンでキティの毛の色を変えていくメッセージに署名したとします。先ほどの例の通り、アリスは最後に署名したメッセージのあとに猫の毛の色がどうなっているかをイーサリアムに対し主張すればいいわけです。
猫の毛皮の色であろうが目の色だろうがイーサリアムブロックチェーンにデプロイされたスマートコントラクトはこれらの変更を理解しなければならないのです。新しい機能や新しい型の『状態遷移』はプラズマコントラクト上のロジックの変更を要するので、今までのプラズマの仕様では、このような機能を足すためには、全てのプラズマコントラクトをもう一度デプロイし直し、みんなの資産を古いプラズマチェーンから新しいプラズマチェーンへマイグレーションしなければなりませんでした。それではセキュアでもスケーラブルでもなく、アップグレードも難しかったのです。
プレディケート: プラズマをもっと便利に!
プラズマチェーンの設計に新しい機能を追加したいと考えたときに、私たちは上記で説明したような『どうやったら簡単にアップグレードできるか問題』に直面しましたが、 ブレインストーミングののち、メインのプラズマチェーンコントラクトを変えることなく新しい機能を追加する簡単な方法を思いつきました。
私たちが議論してきた、さまざまなアップグレードのシナリオにはある共通点がありました。オフチェーンデータはいつもメインチェーンへの間違った主張に対して反証するためにあるものだということです。ある特定の主張に対する反証とは、簡単に言うと、その主張で参照されたステートが期限切れだと示すための証拠です。例えば、ある所有権への反証は、主張をしたユーザーが後で同じ所有権を他の誰かに移したと示す証拠となります。猫の毛皮に関する反証であれば、猫の毛皮はもう既に他の色に変えられたと示す証拠でなければなりません。
私たちの一番のブレイクスルーは、メインのスマートコントラクトでその反証が正当なものかどうか確認しなくてもよいとに気づいたことでした。代わりに、正当性を確認する機能を搭載した他のスマートコントラクトを新たに用意すればいいのです。 つまり新しい機能を足すには、その新機能に必要なロジックを実装した新しいコントラクトを作り、今までのメインコントラクトからその新しいコントラクトを参照すればいいのです。この外部コントラクトをPredicate(プレディケート)と名付けました。
このようにして新しい機能を搭載することは簡単になったのですが、次にある特定のステートアップデートに対してどのプレディケートを使うべきか知る方法を見つけなければならなくなりました。例えば、猫の目の色の変更情報を使っても、猫の毛皮の色に関する主張に対しての反証は行えません。では、どうすればどのプレディケートを使えばいいかわかるのでしょうか?
簡単です。ステートアップデートがどのプレディケートに対応しているものなのか指定するという条件を追加すればいいのです。 そうすれば、猫の毛の色を変えるアップデートメッセージは、例えば『私はこの猫の毛皮の色を青に変えますが、反証は0x123
(←コントラクトアドレス)上にあるプレディケートを使って行なってください。』などというものになります。
ユーザーはどれでも好きなようにプレディケートのアドレスを指定できるので、新しいプレディケートをEthereumにデプロイするだけで誰でも好きな時に新しい機能をプラズマチェーンに足すことができます。反証以外にもプレディケートができることはありますがそれは後で説明することにします。何が重要かというとプレディケートには標準的なインターフェースを実装する必要があるということです。次のセクションでより詳しく見ていきましょう。
プレディケートの具体的な中身
ステートオブジェクト
さてここでプレディケートが実際どのように動いているのか見てみましょう。 プラズマチェーンはステートオブジェクトの積み重ねによって構成されています。 ステートオブジェクトとは2つの項目から構成されるデータの塊です。
predicateAddress
: オブジェクトをコントロールするメインチェーン上のアドレスparameters
: オブジェクトを説明するための任意のデータ
ステートオブジェクトとはつまり資産、つまり、Plasma Cashのnon-fungibleコインの概念を抽象化したものです。Plasma Cashで1つ1つの固有のコインがcoinID
を持っていたように、それぞれのステートオブジェクトもstateID
を保有しています。
説明はこれで以上です! stateID
はプラズマチェーンにデポジットされていったそれぞれのステートオブジェクトに時系列順で割り振られていきますが、parameters
やpredicate
が何であるべきか、などのルールは特にありません。ステートアップデートの集合体がプラズマブロックであり、それらのステートアップデートによって特定のstateID
における新しいstateObject
が定められるのです。
私たちの開発しているプラズマはrange(レンジ)設定のあるCash派生なので、stateUpdate
はstateObject
IDが割り振られた特定のrange内で起こります。
stateUpdate = {
start: uint,
end: uint,
plasmaBlockNumber: uint,
stateObject: stateObject
}
オペレーターがプラズマブロックのハッシュ値を提出する先であるEthereum上のプラズマチェーンコントラクトはverifyUpdate(update: stateUpdate, updateWitness:byte[]) -> bool
という関数を実装しています。これはマークルインクルージョンプルーフ(updateWitness
)というステートアップデートが本当にコミットされたかの証拠があるかをチェックする関数です。
プレディケートのインターフェース
プレディケートは標準的なコントラクトのインターフェースを実装しなければなりません。それらの関数を実際に見ていきましょう。
プラズマコントラクトがする最も重要な作業はステートアップデートの正当性を見極めることです。特に、ブロックを操作しようと思えばできる絶対的な権限を持っているオペレーターが正当なステートアップデートに “侵入”し、stateObject.parameters.owner == operator
と書き換え窃盗することを防がなければなりません。
これを防ぐために、私たちは “state deprication”という概念を設計に取り入れることを思いつきました。それはあるstateIDに対する正当なステートオブジェクトはまだ “deprecate”(無効化)されていない最も昔の(時系列的に前である)ステートアップデートである、というものです。state deprecationはUTXOモデルのブロックチェーンにおいて、UTXO(未使用トランザクションアウトプット)が使用済みトランザクションアウトプットに変わるのに似ています。
このルールを取り入れたことによって、たとえオペレーターが不正にstateObject.parameters.owner == operator
とあとで書き換えたとしても、それより前のステートアップデートstateObject.parameters.owner == alice
が勝つわけです。なぜかというとそのアップデートを期限切れのものだとdeprecate(無効化)できるのはアリスだけであり、アリスはまだ自分のステートアップデートを無効化していないからです。
つまり、プレディケートの中で最も重要な関数はあるステートを無効化できる条件を定義するものです。
verifyDeprecation(stateID: uint, update: stateUpdate, deprecationWitness: bytes)
verifyDeprecation
関数はコミットされたstateUpdateが特定のstateIDにおいて無効化されたかどうかをtrue
/false
値を返して示します。deprecationWitness
はstateObject
が無効化されたかどうかをプレディケートが確かめるために使う、任意のデータが入る変数です。例えば、deprecationWitness
にupdate.stateObject.parameters.owner
から正当な署名を含めなければならないと設定したとしたら、所有者だけがdeprecation(無効化)を承認することができる、と保証することができます。
注意して欲しいのは、この関数はプラズマのexitやdisputeに対する無効化を行うわけではなく、プラズマコントラクトがstateObject
が無効化されているかどうか知りたい時にこの関数を呼び出すということです。
プレディケートのインターフェースには他に3つの関数があります。重要な順に
1.finalizeExit(exit: bytes)
exitが執行された時プラズマコントラクトはそのexitに関係する全ての資産をプレディケートコントラクトアドレスに送信し、この関数を呼び出します。
2. canInitiateExit(stateUpdate: bytes, initiationWitness: bytes) -> bool
この関数はコミットされたステートアップデートに対し、誰が主張を行えるかをプレディケートコントラクト上で制限するものです。例えばownership predicate(コインの所有権の移し変えを行うプレディケート)の場合、canInitiateExit
を呼び出せるのはおそらくその資産の所有者だけに制限されているはずです。
3. getAdditionalDisputePeriod(stateUpdate: bytes) -> uint
この関数はステートアップデートの主張に対する反証期間の延長をしたい時に呼び出すものです。アトミックスワップなど裁判期間を長く要するような複雑なプレディケートにのみ使うものであり、ほとんどの場合この関数は0
を返すだけです。
Predicateの具体例:Ownership Predicate(コインの所有権の移転を行うプレディケート)
全ての事柄は具体例を見ることによって理解しやすくなると思うので、早速1つ例を見てみましょう。最もシンプルなプレディケートはownership predicate (コインの所有権の移転を行うプレディケート)です。あるステートにおいてparameters.owner
に入力され指定されたアカウントはいつでもexitでき、またどんな状態へもステートをアップデートできます。 プレディケートを作る一番最初のステップはステートオブジェクトを設計することです。 ownership predicateの場合、幸運なことにとても簡単で、オブジェクトのparameters
に入る唯一のデータはその時点での所有者のアドレスです。 ownership predicateを使ったステートオブジェクトはおそらくこんな感じでしょう。
OwnedByAlice = {
parameters: {
owner: '0xAliceAddress...',
},
predicate: '0xOwnershipPredicateAddress...'
}
実装しなければならない最も重要な関数はverifyDeprecated
です。verifyDeprecated
は任意のdeprecationWitness
を受け取ります。ownership predicateの場合、正しいdeprecationWitness
は state.parameters.owner
からの新しいstateUpdate
への合意を示す署名 新しいstateUpdate
が後ろのプラズマブロックにコミットされたというプルーフ(インクルージョンプルーフ)
を持っています。 verifyDeprecated
関数は上記の署名と、Merkle proofが正しく揃っているかどうか確認しなければならないということです。
まとめると、以下の図ようにして所有者が所有権を無効化し新しいアップデートを承認すると説明することができます。
残りの関数はかなりシンプルなものとなっています。
canInitiateExit
はexit申請者が所有者であることを確認し、finalizeExit
は資産をその所有者に送ります。そしてgetAdditionalDisputePeriod
は返り値0
を返します。
私たちがPythonで実装したシンプルなownership predicateの実際のコードはこのような感じです。シンプルさを追求するためPythonで実装しましたが、SolidityやVyperでも簡単に書くことができます。
見てわかる通り、上で説明した全てのインターフェースを実装済みです。思うほど難しいわけではないのです。はい、というわけで移転可能な資産の所有権を表したプレディケートをつくることができましたね。ここまでのほとんどのロジックは今までのプラズマコントラクトと同じです。ETHDenverにてプロトタイプも作ってみましたが、ほぼ今まで書いてきたコードを寄せ集めて書く順番を変更する作業でした。
未来へ向けて
私達は、プラズマにアップグレード性と汎用性を与えるプレディケートはプラズマを単なるペイメントチャネルから汎用性の高いステートチャネルへと押し上げるための大きな飛躍になると信じています。
それだけではなく、他にもたくさんプラズマ全体の研究を深めていけるようなプレディケートの設計に関する研究項目があります。
- ステートチャネル
- DEX
- Defragmentation
- Nested Plasma
- P2P CDPコントラクト
しかし、プレディケートはプラズマをベースにしたアプリケーションにしか対応できないため、全てを解決する銀の弾丸ではありません。それでもとても大きな影響を与える技術であると思いますし、Plasma Cash派生以外のプラズマにも実装できるでしょう。
これはプラズマのエコシステム全体での汎用性に繋がると考えております。state deprecationの設計を兼ね備えているプラズマであればプレディケートコントラクトを共有し、相互運用できるでしょう。
レイヤー2スケーリングソリューションは全てオフチェーンデータを使い未来のオンチェーンのステートを保証するという設計で動いています。ステートチャネルにおいて古いステートが署名によって無効化されたり、プラズマでコミットされたり、いろいろなレイヤー2ソリューションが最終的に目指すことはこの一点に尽きます。私達はその都度個別につなぎを作らなくても、全てのレイヤー2ソリューションが共通のインターフェースを通して繋がっていく未来を描いています。全てはインターオペラビリティのために、そしてインターオペラビリティはスケーリングというゴールのために!
参考文献
Pythonのシミュレーション: https://github.com/plasma-group/research/tree/master/gen-plasma
技術的な仕様: https://pigi.readthedocs.io/en/latest/src/specs/generalized-plasma-state.html
Plasma Groupの運営するPlasma Contributorsテレグラムグループへの参加はこちらから: https://t.me/plasmacontributors
Thanks to:
この記事を翻訳するにあたり協力してくれたCryptoeconomics Labチームのメンバーに感謝申し上げます。
Cryptoeconomics LabでもPlasma Groupと協力し、できる限り仕様を統一し汎用的なプラズマのフレームワークPlasma Chamberを開発しています。
Cryptoeconomics Labの運営する開発者向けPlasma Chamber日本語テレグラムグループへの参加はこちらから: https://t.me/plasmaqanda
弊社の技術的なアップデートもぜひMediumやGithubなど以下のソースをチェックして追ってみてください。
●Cryptoeconomics Labのリンク集
Github:
Medium:
Twitter: