# Verkle tree 実装詳細 ## key-value 型 DB に対する Verkle tree - [Verkle tree integration - HackMD](https://notes.ethereum.org/@vbuterin/verkle_tree_eip) - [gballet/go-verkle](https://github.com/gballet/go-verkle) key, value が 32 bytes ずつのテーブルの中身について inclusion proof したい. これができると stateless client の実装に役立つ. Verkle tree の作成手順を図, 及び python コードで示した. vector commitment では基本的に楕円曲線を用いるので, key, value が 32 bytes ずつのテーブル $d = \{(k_{\lambda}, v_{\lambda})\}_{\lambda \in \Lambda}$ に関する Verkle tree を作成したい場合に, 最下層を若干工夫する必要がある. この部分を extension (Verkle) tree として構成し, $d^{\prime} = \{(\mathrm{stem}, D^{(\mathrm{stem})})\}_{\mathrm{stem} \in \mathsf{Stems}}$ というテーブルに帰着させる. ただし $\mathsf{Stems}$ とは, 任意の $k_{\lambda} \ (\lambda \in \Lambda)$ を $256^{31}$ で割った余りを含み, かつそのようなもののみを含む集合とする. $$\mathsf{Stems} := \{ p \in [0, 256^{31}) \mid k_{\lambda} = p + s \cdot 256^{31} \text{ for some } s \in [0, 256), \lambda \in \Lambda \}$$ extension Verkle tree と $d^{\prime}$ から構成した Verkle suffix tree とを合わせて, $d$ の Verkle tree が構成される. 深さの最大値は root node を含めなければ 33 (= 2 + 31) であるが, テーブルの key $k_{\lambda}$ たちが十分に散らばっているほど小さくなる. 理想的な例としては, $256^2$ 個のデータがあって, それらの key の suffix 2 bytes が全て異なれば, 深さ 4 の Verkle tree になる. ![vercle_tree_construction](https://i.imgur.com/kZA0Bqh.jpg) <!-- 下記コード中の compute_vector_commitment 関数は単なる hash 関数として実装している. Merkle proof よりも短い proof を作成したいという目的のためには, より良い commitment scheme を選ぶ必要がある. ```python import hashlib from typing import Dict, List bytes32 = bytes bytes31 = bytes byte = bytes VERKLE_NODE_WIDTH = 256 EXTENSION_MARKER = 1 def compute_vector_commitment(children: List[int]) -> int: # pseudo code digest = hashlib.sha3_256(b"".join([value.to_bytes(32, 'little') for value in children])).hexdigest() return int(digest, 16) def compute_extension_tree_root(stem: bytes31, values: Dict[byte, bytes32]) -> int: sub_leaves = [0] * 512 for suffix, value in values.items(): suffix_int = int.from_bytes(suffix, 'little') sub_leaves[2 * suffix_int] = \ int(2**128) + int.from_bytes(value[:16], 'little') sub_leaves[2 * suffix_int + 1] = \ int.from_bytes(value[16:], 'little') C0 = compute_vector_commitment(sub_leaves[:256]) C1 = compute_vector_commitment(sub_leaves[256:]) main_leaves = [ int(EXTENSION_MARKER), int.from_bytes(stem, 'little'), C0, C1 ] + [0] * 252 return compute_vector_commitment(main_leaves) def compute_main_tree_root(data: Dict[bytes32, int], prefix: bytes) -> int: if len(data) == 0: return 0 elif len(data) == 1: return list(data.values())[0] else: sub_commitments = [ compute_main_tree_root( { key: value for key, value in data.items() if key[:len(prefix) + 1] == prefix + bytes([i]) }, prefix + bytes([i]) ) for i in range(VERKLE_NODE_WIDTH) ] return compute_vector_commitment(sub_commitments) def compute_verkle_commitment(data: Dict[bytes32, bytes32]) -> int: stems = set(key[:-1] for key in data.keys()) data_as_stems = dict() for stem in stems: commitment_data = dict() for i in range(VERKLE_NODE_WIDTH): key = stem + bytes([i]) if key in data: commitment_data[bytes(i)] = data[key] data_as_stems[stem] = compute_extension_tree_root( stem, commitment_data) return compute_main_tree_root(data_as_stems, bytes([])) if __name__ == '__main__': data = {0: 2, 1: 15, 2: 209, 256: 83} table = {} for key, value in data.items(): table[key.to_bytes(32, 'little')] = value.to_bytes(32, 'little') root = hex(compute_verkle_commitment(table)) assert root == '0x70dbdb60678021614a6d5455c5c6e4dfd69fdc8d2a53790947c60f5dea3d7dcd' ``` --> <!-- ## Verkle Proof の実装 proof の構築方法は下記などを参考にしたが, 実装の細部まで理解できたわけではない. [research/verkle_trie.py](https://github.com/ethereum/research/blob/master/verkle_trie_eip/verkle_trie.py) この実装では, vector commitment として pedersen commitment なるものを用いている. そこでは bandersnatch という楕円曲線が用いられている. [bandersnatch/bandersnatch](https://github.com/zhenfeizhang/bandersnatch/tree/65823a69d6c4c7612d2f2d3d21ce87f2b319fcf0/bandersnatch) かなり複雑な実装に見えるが, セキュリティ要件の都合で必要になるのだろうか? --> ## コード ### CreateIPAProof ![Figure 9](https://hackmd.io/_uploads/rJX_u9QKK.jpg) ![Figure 10](https://hackmd.io/_uploads/B1mOu9mYY.jpg) ### CheckMultiProof ![Figure 11](https://hackmd.io/_uploads/r1DOdcmtF.jpg) ![Figure 12](https://hackmd.io/_uploads/HkD_dqQtt.jpg) ### Insert ![Figure 14](https://hackmd.io/_uploads/HkZeDroFY.jpg) ![Figure 15](https://hackmd.io/_uploads/Sy-xwHsKF.jpg) ![Figure 16](https://hackmd.io/_uploads/HyZeProKt.jpg) ### ComputeCommitment ![Figure 17](https://hackmd.io/_uploads/Bk-gProFK.jpg) ![Figure 18](https://hackmd.io/_uploads/ryZgwriFK.jpg) ## Non-inclusion proof non-inclusion proof も行える Verkle SMT にしたいならば, $E^{(\mathrm{suffix})}$ が empty node のときに値 0 を結びつけるのではなく, $(0, \mathrm{stem}, 0, \ldots, 0)$ から構成した node $D^{(\mathrm{stem})}$ の commitment を結びつけるなどすれば良さそうである. さらに, extension tree の各 leaf にも non-inclusion flag を含めておく必要がある. ![extension_tree_for_non_inclusion_proof](https://i.imgur.com/KWpKF71.png)