Ethereumのスケーラビリティを考えた際にネックになるのが巨大なステートストレージ。利用者は1回だけ手数料を積んだトランザクションを発行するだけだけど、ネットワークに参加するフルノードは以降ずっとそのデータを維持しなければならず、ステートの成長とともに維持コストも膨れ上がりネックになる。
この問題へのアプローチの1つとして、ステートレスクライアントというアイディアがある。全ステートに対する暗号学的なコミットメントが、Ethereumのブロックヘッダーにステートルートという形で記録されている。ブロックの提供者に各トランザクションがアクセスするステート自体とそれが有効なステートであることの証明をセットで提供してもらうことで、ノードがそのストレージにステートを保持しなくて済むようにしようというもの。
Merkle Patricia Trieの包含証明
あるステートが有効かどうかはMerkle Patricia Trieのアイテムの包含証明を提供することで検証可能だ。web3-eth
でもこのプルーフを取得するための関数getProofが提供されている。
Bitcoinのような二分木であれば、各ノードは2つの子ノードしか持たないため、ルートから対象のリーフノードまでのパス上にある兄弟ノードの数分の各ノードのハッシュ値がプルーフになる()。
一方、Merkle Patricia Trieのような16-ary(hex-ary)ツリーは、16個の子ノードを持つ。つまり各ノードが最大16個の子ノードのハッシュ値を持っている。そのため、単一のアイテムの包含証明には、ツリーのルートからそのアイテムがあるリーフノードまでのツリーの経路上のノードデータがプルーフとして必要になる。厳密にいうと、ルートノードは既知であるから必要なく、それ以外の経路上のノードデータの内、関係する子ノード以外の兄弟ノードのハッシュ値になるので15個のハッシュ。これらがあれば、リーフノードのデータから各ノードのハッシュを計算し、上位ノードの兄弟ノードのハッシュ値(プルーフ)を計算しながらツリーをたどり、ルートノードの子ノードのハッシュ値と合致すれば、アイテムがツリーに包含されていることを検証できる。ちなみに↑のgetproof
関数は、兄弟分とかは関係なく経路上のノードの全データを返してるっぽい。
ステートレスクライアントのようなアプローチをする場合、現状のMerkle Patricia Trieだと上記のように1つのプルーフあたりのデータが必要になり、これがネックになる。
Verkle Trie
↑の課題を解消するために提案されているのがVerkle Trieという木構造とKZGコミットメントのアキュムレーター構造を組み合わせたもの↓
https://notes.ethereum.org/_N1mutVERDKtqGIEYc-Flw
Verkleという名前はVectorとMerkleから取った模様。Verkle Trieでは、プルーフの生成に多項式コミットメントスキームの一種であるKZGコミットメントを使用している↓
techmedia-think.hatenablog.com
一般的に、マークルツリーはn
個の要素に対するベクトルコミットメントとして機能するが、多項式コミットメントも、n
次の多項式にコミットするように構成すれば、n
個の要素に対するベクトルコミットメントとして機能する。
そしてこの多項式コミットメントスキームの利点は、ベクトル内のアイテムの包含証明が、アイテムの数に依存せず、一定サイズになるという点(アキュムレーターのように機能する)。これによりステートレスクライアント導入にあたって、ステートの存在証明のサイズを小さな一定サイズにすることができる。
Verkle Trieのアイディアは、16個の子ノードを保持するMerkle Patricia Trieの内部ノードをKZGコミットメントに置き換えたKZGコミットメントのツリーを構成しようというもの。
中間ノードのKZGコミットメント
システムパラメータとして16個の楕円曲線上の点が与えられるので(Gはそのベースポイント)、それと16個の子ノードの各データ(おそらくハッシュ値)をを使って、
を計算すれば、これがその中間ノードのKZGコミットメントになる。
見ての通り、このコミットメントは楕円曲線の1要素なので、サイズは使用する楕円曲線に依存するがBLS12-381とかだと48バイトくらいのはずで(KZGコミットメントにはペアリングが可能な曲線が必要)、既存の32バイトのハッシュ値×16個よりもとても小さくなる。
また、KZGコミットメントへの置き換えにより、包含証明に必要なプルーフのデータがからまで削減できる。
Verkle Trieの包含証明
Vercle Trie内にアイテムが存在するかは、以下のデータを使って検証される。
これらのデータを使って
- リーフノードのデータが、親ノードのKZGコミットメントに含まれているか。
- 1の親ノードのデータがさらにその親のKZGコミットメントに含まれているか。
- これをルートまで繰り返す。
ここまでだと、中間ノードの数分のコミットメントとプルーフが必要に思えるが、Multi-proofsという仕組みを利用すると小さな単一のプルーフ(200バイト以下)でこれらの証明が可能になるみたい↓
https://notes.ethereum.org/nrQqhVpQRi6acQckwm1Ryg
この辺り、また詳しく追っていきたい。
Trusted Setup
ただ、KZGコミットメントは、↑のブログ記事に書いたようにTrusted Setupを必要とする。↑のような16-aryツリーを構成する場合は、16個のシークレットパラメータ()を予め、誰もその値を知りえないようMPCなどで計算する必要がある。
Merkle Patricia Trieの課題から二分木に移行しようという議論もあったけど、Verkle Trieは、16-aryツリーのままアイテムの包含証明のサイズを削減できる新しいアプローチになる。