# Verkle trees 仕様 現存する Verkle tree 実装のデータ構造に関する仕様をまとめたものである. 参考にしたのは下記の実装である (完全な Rust 実装は見つからなかった). - [gballet/go-verkle](https://github.com/gballet/go-verkle) その中で用いられている IPA (内積証明) では, 下記の実装に依存している. - [crate-crypto/go-ipa](https://github.com/crate-crypto/go-ipa) ドキュメントによると, 実際に Ethereum Mainnet の全アカウントの状態に関する Verkle tree を作ったらしい. Verkle tree は $k$ 分木を考え, $d$ をその深さとする. すなわち, leaf の数は最大で $n := k^d$ である. 上の実装においては, $k = 256$, $d \leq 256$ となっている. 各 leaf には $\mathbb{F}_r$ の元が格納できる. $\lvert \mathbb{F}_r \rvert < 2^{256}$ なので, 32 bytes のデータはそのままでは格納できないことに注意してください. 解決策は [key-value 型 DB に対する Verkle tree](https://hackmd.io/@totorozk/S1L-b-YOF#key-value-%E5%9E%8B-DB-%E3%81%AB%E5%AF%BE%E3%81%99%E3%82%8B-Verkle-tree) を参考にしてください. このページでは, 実装のインターフェースとデータ構造に焦点を当てて説明します. [実装詳細](https://hackmd.io/@totorozk/HyUg8OnFt)や[検証コスト](https://hackmd.io/@totorozk/B1d5w31KY)などは別にまとめてあります. ## go-verkle のインターフェース Verkle tree の構築や更新, commitment の作成などのプロトコルを担当する. 各 tree node の構築や更新を行うには, 少なくともその node の siblings の hash 値を知っている必要がある. $\mathsf{Insert}$ は, 指定した node の新規挿入, あるいは値の更新を行う. この段階では commitment を計算しない. $\mathsf{ComputeCommitment}$ は, 指定した node の commitment を計算する. ```go func MakeVerkleMultiProof(tree VerkleNode, keys [][]byte) (*Proof, []*Point, []byte, []*Fr) { return proof, cs, zs, ys; } func VerifyVerkleProof(proof *Proof, cs []*Point, zs []byte, ys []*Fr) bool { return isOk; }; ``` ## go-ipa のインターフェース 与えられた Verkle tree の inclusion proof を生成, 検証するプロトコルを担当する. $\mathtt{zs} \in [0, k]^{d}$ は, inclusion proof を行いたい key の値を $k$ 進数表記した文字列とする. $$ \mathsf{CreateMultiProof}: \mathbb{G}^{d} \times (\mathbb{F}_r^{k})^{d} \times [0, k]^{d} \to (\mathbb{G}^{\log_2 d} \times \mathbb{G}^{\log_2 d} \times \mathbb{F}_r \times \mathbb{G}) $$ は prover によって呼び出される. prover は Verkle tree の状態をすべて持っているとする. Verkle tree の key が指す node から root node までの path に沿った internal node たちの情報から, $\mathtt{ys}$, $\mathtt{fs}$, $\mathtt{commitments}$ を計算できる. 多項式 $f_i$ は, 深さ $i$ 番目の internal node の $z \in [0, k]$ 番目の child に値 $f_i(z) \in \mathbb{F}_r$ が格納されていることを意味する. 多項式 $f_i$ の Pedersen commitment を $C_i$ とする. $\mathtt{zs} = (z_i)_{i \in [1, d]}$, $\mathtt{ys} = (y_i)_{i \in [1, d]}$, $\mathtt{fs} = (f_i)_{i \in [1, d]}$, $\mathtt{commitments} = (C_i)_{i \in [1, d]}$ とおく. ただし, 任意の $i \in [1, d]$ に対して, $f_i(z_i) = y_i$ を満たすとする. これらの情報をもとに prover は ``` multiProof = CreateMultiProof(commitments, fs, zs) ``` を計算する. 出力 $\mathtt{multiProof}$ は 4 つ組 $(L, R, A, D)$ で構成される. $$\mathsf{CheckMultiProof}: (\mathbb{G}^{\log_2 d} \times \mathbb{G}^{\log_2 d} \times \mathbb{F}_r \times \mathbb{G}) \times \mathbb{G}^{d} \times \mathbb{F}_r^{d} \times [0, k]^{d} \to \{ 0, 1 \} $$ は verifier によって呼び出される. Verkle tree の状態をすべて持っている verifier であれば, $\mathtt{zs}$ をもとに $\mathtt{commitments}$, $\mathtt{ys}$ は verifier 自身で計算できるが, そうでなければ prover が $\mathtt{multiProof}$ と一緒に渡してあげる必要がある. これらの情報をもとに verifier は ``` isOk = CheckMultiProof(multiProof, commitments, ys, zs) ``` を計算し, $\mathtt{isOk} = 1$ であるときに限り, 与えられた `multiProof` を受理する. ## メモ ![Figure1](https://hackmd.io/_uploads/HJCc89fKF.jpg) ![Figure2](https://hackmd.io/_uploads/SkbsUqfKY.jpg)