# intmax operator 旧仕様 (PlonK 版) intmax の operator 側の仕様について, account や transaction の表現方法などを記載しています. ## Notation $\mathbb{N}$ は 0 以上の整数全体からなる集合, $\mathbb{N}_{m} := [0, 2^m - 1]$ $(m > 1)$ は $m$ bits で表せる数値全体からなる集合, $\mathbb{B}_{n}$ $(n \geq 1)$ はちょうど $n$ bytes のバイト列全体からなる集合, $\mathbb{B} = \bigcup_{n = 0}^{\infty} \mathbb{B}_{n}$ は任意の長さのバイト列全体からなる集合とする. $\mathbb{F}_r$ は位数 $r$ の有限体とする. $E$ は楕円曲線, $E(\mathbb{F}_r)$ は $E$ の $\mathbb{F}_r$ 有理点全体からなる集合とする. $E(\mathbb{F}_r)$ 上の二項演算 $+$ をうまく定めることにより, $E$ の無限遠点 $\mathcal{O}_{E}$ を単位元とする可換群の構造が入る. 楕円曲線 $E(\mathbb{F}_r)$ には素数位数 $s$ の元 $B$ が存在するとし, $B$ が生成する巡回群を $\langle B \rangle := \{ B, 2B, \ldots, (s-1)B, sB = \mathcal{O}_{E} \}$ で表す. その任意の元 $G \in \langle B \rangle$ は, $$ sG = \mathcal{O}_{E} $$ をみたす. $E(\mathbb{F}_r)$ の元は, (Reduced) Twisted Edwards Form の場合, $\mathbb{F}_r^2 \cup \{ \mathcal{O}_{E} \}$ の元として表現できる. 集合 $X, Y$ について, 対応 $f: X \to Y$ としてあり得るもの全体の集まりを $\mathsf{Map} [X, Y]$ で表す. <!-- value が $\mathbb{F}_r$ の元であるような深さ $n$ の Merkle tree 全体からなる集合を $\mathsf{MT}_n$ と表す. $\mathsf{Map} [2^n, \mathbb{F}_r]$ --> <!-- key, value がともに $\mathbb{F}_r$ の元であるような sparse Merkle tree (SMT) 全体からなる集合を $\mathsf{SMT}$ と表す. --> <!-- 例えば, 集合 $\mathsf{MT}_1$ には $\{ (0, v_0), (1, v_1) \}$ という 2 つの leaf を持った Merkle tree が属する. --> <!-- 全ての key に $0 \in \mathbb{F}_r$ が格納されている Merkle tree を $\mathbf{0}$ と表す. また, SMT のいくつかの key に対応する値は存在しないことがある (そのことを記号 $\bot$ で表す). どの key に対応する値も持たない SMT は**空**であると呼び, 空集合と同じ記号 $\emptyset$ で表すことにする. 空である SMT の root hash は $0 \in \mathbb{F}_r$ とする. --> SMT の leaf に別の SMT の root hash が含まれるような 2 層の SMT を **layered SMT** と呼ぶことにする. <!-- このとき, key が 2 つの $\mathbb{F}_r$ の元の組で表される. このような layered SMT 全体からなる集合を $$ \mathsf{LSMT} := \mathsf{Map} [\mathbb{F}_r \times \mathbb{F}_r, \mathbb{F}_r \cup \{ \bot \}] $$ と表す. --> ## Setup (Operator) operator は次の 4 つの (sparse) Merkle tree を扱う. $$ \mathtt{commonly\_shared\_storage} \leftarrow \emptyset \in \mathsf{Map} [\mathbb{F}_r, \mathbb{F}_r \cup \{ \bot \}] $$ `user_asset_storage` は `contract_address` に対応する `internal_user_asset_root` を格納する SMT. $$ \mathtt{user\_asset\_storage} \leftarrow \emptyset \in \mathsf{Map} [\mathbb{F}_r, \mathbb{F}_r \cup \{ \bot \}] $$ `contract_asset_storage` は `contract_address` と `variable_index` に対応する値を格納する layered SMT. $$ \mathtt{contract\_asset\_storage} \leftarrow \emptyset \in \mathsf{Map} [\mathbb{F}_r, \mathsf{Map} [\mathbb{F}_r, \mathbb{F}_r \cup \{ \bot \}]] $$ `asset_diff_storage` は `tx_hash` に対応する `asset_diff_root` を格納する SMT. $$ \mathtt{asset\_diff\_storage} \leftarrow \emptyset \in \mathsf{Map} [\mathbb{F}_r, (\mathbb{F}_r \times \mathbb{F}_r) \cup \{ \bot \}] $$ また, これらの Merkle tree root をまとめた `world_state_root` を初期化する. $$ \mathtt{world\_state\_root} \leftarrow \mathbf{Poseidon}_{4}(0, 0, 0) \in \mathbb{F}_r$$ operator はこれらを常に記憶し, user からの要求に応じて更新する. state tree の詳細は Appendix で説明している. ## Setup (User) user は適切な方法で秘密鍵を生成する. $$ \mathtt{user\_private\_key} \overset{$}{\leftarrow} \mathbb{F}_s $$ この秘密鍵はトランザクションを生成する際に毎回必要となり, 他者に教えてはならない. この秘密鍵を用いて自身のアカウント (`user_account`) を用意する. (address を連番の登録制にする方向性もあり) $$ \mathtt{user\_public\_key} \leftarrow \mathbf{PrvToPub}( \mathtt{user\_private\_key}) \in \langle B \rangle \subset E(\mathbb{F}_r) \\ \mathtt{user\_address} \leftarrow \mathbf{PubToAddr}( \mathtt{user\_public\_key}) \in \mathbb{B}_{20}, \\ \mathtt{user\_nonce} \leftarrow 0 \in \mathbb{N}_{64} \\ \mathtt{user\_account} = (\mathtt{user\_private\_key}, \mathtt{user\_public\_key}, \\ \mathtt{user\_address}, \mathtt{user\_nonce}) $$ さらに, user は自身の state を管理するための layered SMT を保持しておく. $$ \mathtt{internal\_user\_asset\_storage} \leftarrow \emptyset \in \mathsf{Map} [\mathbb{B}_{20}, \mathsf{Map} [\mathbb{F}_r, \mathbb{F}_r \cup \{ \bot \}]] $$ one-time account は今回使用しないので, 通常の account と同じとする. $$ \mathtt{ot\_private\_key} = \mathtt{user\_private\_key}, \\ \mathtt{ot\_public\_key} = \mathtt{user\_public\_key}, \\ \mathtt{ot\_address} = \mathtt{user\_address} $$ ## Transaction まず, 全体の処理を通してみると, 次のようになっている. ```mermaid sequenceDiagram User->>OneTimeAccount: tx, nonce OneTimeAccount->>Operator: signed_tx Operator->>Operator: calc_state_diff Operator->>OneTimeAccount: user_asset_diff OneTimeAccount->>User: user_asset_diff User->>User: merge_user_asset <br /> & sign_diff User->>Operator: signed_tx, internal_user_state_root, signed_user_asset_diff Operator->>Operator: update_user_state & <br /> merge_shared_state ``` transaction は下記の要素から構成される(本当は one-time account で署名するが, 今回は user の通常の鍵で署名する). $$ \mathtt{tx} = ( \mathtt{ot\_public\_key}, \mathtt{user\_nonce}, \\ \mathtt{contract\_address}, \mathtt{function\_signature}, \mathtt{calldata}) $$ ただし, $$ \mathtt{contract\_address} \in \mathbb{B}_{20}, \\ \mathtt{function\_signature} \in \mathbb{B}_{4}, \\ \mathtt{user\_nonce} \in \mathbb{N}_{64}, \\ \mathtt{calldata} \in \mathbb{B} $$ transaction の digest は Poseidon hash で計算する. ただし, $(c_0, \ldots, c_3) \in (\mathbb{B}_{32})^4$ は `calldata` を 128 bytes になるように右側を 0 パディングし, 32 bytes ごとに分割したものである. $$ \mathtt{nonce\_address\_sig} \leftarrow \mathbf{ToFr}(\mathtt{user\_nonce} \| \mathtt{contract\_address} \| \\ \mathtt{function\_signature}) \in \mathbb{F}_{r} $$ ただし, `to_fr` はバイト列を Big Endian で $\mathbb{F}_{r}$ 型に変換する. > `user_nonce` の上位 3 bits は無視されるが, nonce は連番で使用されるため, 問題ないと考えている. $$ \mathtt{tx\_hash} \leftarrow \mathbf{Poseidon}_6(\mathtt{nonce\_address\_sig}, c_0, c_1, c_2, c_3) \in \mathbb{F}_r \\ $$ これに署名する. $$ \mathtt{tx\_signature} \leftarrow \mathbf{SignEddsaPoseidon}(\mathtt{ot\_private\_key}, \\ \mathtt{tx\_hash}) \in \mathsf{Signature} $$ ここで, $\mathsf{Signature}$ は $$ \mathsf{Signature} := \{ (R, S) \mid R \in E(\mathbb{F}_r), S \in \mathbb{F}_s \}$$ なる集合を表す. $$ \mathtt{signed\_tx} = (\mathtt{tx}, \mathtt{tx\_hash}, \mathtt{ot\_public\_key}, \mathtt{tx\_signature}) $$ を operator に送信する. > 事前に残高を減らすようなマージが行われていることを証明しなければならない. ## Diff operator は受け取った transaction の内容を見て, user asset storage の変化 `asset_diff` を計算する. 変更があるすべての state について, contract address, variable index の組とそれに対応する差分 `asset_diff` を列挙する. $$ \mathtt{world\_state\_diff} \leftarrow \mathbf{CalcTransition}(\mathtt{tx}) \in \mathsf{WorldStateDiff} $$ $$ \mathsf{WorldStateDiff} := \mathsf{SharedStateDiffList} \times \\ \mathsf{UserAssetDiffList} \times \mathsf{ContractAssetDiffList} $$ $$ \mathsf{SharedStateDiffList} := \mathsf{Map}[\mathbb{B}_{20} \times \mathbb{F}_r, \mathbb{F}_r] \\ \mathsf{UserAssetDiffList} := \mathsf{Map}[\mathbb{B}_{20} \times \mathbb{B}_{20} \times \mathbb{F}_r, \mathbb{F}_r] \\ \mathsf{ContractAssetDiffList} := \mathsf{Map}[ \mathbb{F}_r, \mathbb{F}_r] $$ `world_state_diff` の各要素をそれぞれ, `shared_state_diff`, `user_asset_diff`, `contract_asset_diff` とする. `world_state_diff` の digest を SMT で作る. $$ \mathtt{world\_state\_diff\_root} \leftarrow \mathbf{Commit}_{\mathsf{SMT}}(\mathtt{world\_state\_diff}) \in \mathbb{F}_r $$ operator は `diff_storage` に `(key, value) = (tx_hash, world_state_diff_root)` というエントリを追加して, その commitment `world_state_diff_root` と `transaction` 及び `world_state_diff` をどこか安全な場所に公開する. $$ \mathtt{diff\_storage} \leftarrow \mathbf{Insert}(\mathtt{diff\_storage}, \\ (\mathtt{tx\_hash}, \mathtt{world\_state\_diff\_root})) $$ > 並行処理は難しそう. operator はこの `world_state_diff` (と `world_state_diff_root`) を user に送信する. user は operator から渡された `world_state_diff` に含まれる `user_asset_diff` のうち自身に関わるものと (必要であればまだ反映していない diff) によって自身の user asset storage を更新し, transaction を取り込んだことを保証する. > user は `world_state_diff` に含まれる `user_asset_diff` のうち自身に関わるものの commitment `user_asset_diff_root` に署名する. > 他人 `receiver` の asset を増加させる差分 `receiver_asset_diff` は sender が直接渡してあげ, それが全体の `asset_diff` の一部であることの inclusion-proof もする. `receiver` はそれをマージして署名を operator に提出し, sender の `asset_diff` への commitment と合わせて token を受け取ったことになる. `user_state_diff` に従って, user は自身で管理している internal user asset storage を更新して, internal user asset storage の layered SMT root `internal_user_asset_root` を operator に送信する. $$ \mathtt{internal\_user\_asset\_storage} \leftarrow \mathbf{Merge}(\mathtt{internal\_user\_asset\_storage}, \\ \mathtt{asset\_diff}) \\ \mathtt{internal\_user\_asset\_root} \leftarrow \mathbf{Commit}_{\mathsf{LSMT}}(\mathtt{internal\_user\_asset\_storage}) $$ さらに, user は `world_state_diff_root` に署名して operator に送信する. $$ \mathtt{diff\_signature} \leftarrow \mathbf{SignEddsaPoseidon}(\mathtt{user\_private\_key}, \\ \mathtt{world\_state\_diff\_root}) \in \mathsf{Signature} \\ \mathtt{signed\_diff} = (\mathtt{world\_state\_diff}, \mathtt{world\_state\_diff\_root}, \\ \mathtt{user\_public\_key}, \mathtt{diff\_signature}) $$ > なぜ, user は asset diff に署名してそれを見たことを operator に納得させないといけないのか? これをしない場合, exit 時の user asset storage がどこまでの差分を既にマージした状態なのかがわからなくなる. operator は user asset storage, contract asset storage, commonly shared storage をそれぞれ更新する. > 複数人の update を一定期間受け付ける. 理想的には 5 秒間隔. $$ \mathtt{user\_state\_storage} \leftarrow \mathbf{Update}(\mathtt{user\_state\_storage}, \\ (\mathtt{user\_address}, \mathtt{user\_asset\_root})) \\ \mathtt{contract\_asset\_storage} \leftarrow \mathbf{Merge}(\mathtt{contract\_asset\_storage}, \\ \mathtt{contract\_state\_diff}) \\ \mathtt{commonly\_shared\_storage} \leftarrow \mathbf{Merge}(\mathtt{commonly\_shared\_storage}, \\ \mathtt{shared\_state\_diff}) $$ > それと同時に `signed_diff` を保管する. (不要?) operator は world state を更新し, 公開する. $$ \mathtt{world\_state\_root} \leftarrow \mathbf{Poseidon}_{4}( \\ \mathbf{Commit}_{\mathsf{LSMT}}(\mathtt{user\_asset\_storage}), \\ \mathbf{Commit}_{\mathsf{LSMT}}(\mathtt{contract\_asset\_storage}), \\ \mathbf{Commit}_{\mathsf{SMT}}(\mathtt{commonly\_shared\_storage})) $$ user asset root から world state root までの Merkle proof を user に渡す. ただし, user が online でない場合, 一時的なストレージ (ERC4844 Shard Blob) に保管する. ## Rollup > rollup について ## Exit > exit について ## Appendix ### JSON $\mathbb{N}_{m}$ や $\mathbb{B}_{n}$ の元は原則 `0x` 接頭辞がついた 16 進数 (hex) 文字列として扱う. $\mathbb{F}_r$ も普通の 256 bits 数値 $\mathbb{N}_{256}$ と見做して扱う. 楕円曲線上の点 $G \in E(\mathbb{F}_r)$ は $\mathtt{compress\_point}(G)$ によって 256 bits 数値 $\mathbb{N}_{256}$ と見做す. $\mathtt{user\_asset\_diff} \in \mathsf{StateDiffList}$ の serialize は, $\mathtt{diff} := \mathtt{user\_asset\_diff}[x] \neq 0$ なる全ての $x = ( \mathtt{contract\_address}, \mathtt{variable\_index}) \in \mathbb{B}_{20} \times \mathbb{F}_r$ について, 連想配列 `{ address: contract_address, key: variable_index, diff: diff }` を(順序を気にせず)列挙した配列として扱う. 例えば, `user_asset_diff.get(("0x00...00", 1)) == 10`, `user_asset_diff.get(("0x00...00", 3)) == -20` であり, その他の差分が 0 の場合, ``` [ { address: "0x0000000000000000000000000000000000000000", key: 1, diff: 10 }, { address: "0x0000000000000000000000000000000000000000", key: 3, diff: -20 } ] ``` となる. ### Account 鍵を生成するための楕円曲線は, Alt BabyJubjub BN-256 ([EIP 2494](https://eips.ethereum.org/EIPS/eip-2494#generator-point)) を採用し, 次のパラメータを用いる. $E: a^{\prime}x^2 + y^2 = 1 + d^{\prime}x^2y^2$ (Reduced Twisted Edwards Form) `d' = 0x1aee90f15f2189693df072d799fd11fc039b2959ebb7c867d075ca8cf4d7eb8e` `a' = -1` `r = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001` `s = 0x60c89ce5c263405370a08b6d0302b0bab3eedb83920ee0a677297dc392126f1` base point としては次の元が選ばれている. ```md # Reduced Twisted Edwards Form B = ( 0x1561ff836ce19d358a4eb7a4c199e94c377c749ae6f2a277f1f9195afe553f9f, 0x25797203f7a0b24925572e1cd16bf9edfce0051fb9e133774b3c257a872d7d8b ) # Twisted Edwards Form B = ( 0xbb77a6ad63e739b4eacb2e09d6277c12ab8d8010534e0b62893f3f6bb957051, 0x25797203f7a0b24925572e1cd16bf9edfce0051fb9e133774b3c257a872d7d8b ) ``` また, この楕円曲線 $E(\mathbb{F}_r)$ は位数 $8s$ の元 $G_0$ をもち, $B = 8G_0$ が成り立っている. > それゆえ, $B$ には `base8` という変数名がついている. <!-- EdDSA 署名には, このような $2^c s$ ($c$ は正整数, $s$ は十分に大きい素数) の形の位数を持つような曲線が選ばれる[[2](https://ed25519.cr.yp.to/ed25519-20110926.pdf)]. --> ```md # Reduced Twisted Edwards Form G0 = ( 0x0b068376907718bbf713937de82f368c0f049cdf93d705e8294891fba133936f, 0x0c19139cb84c680a6e14116da06056174a0cfa121e6e5c2450f87d64fc000001 ) # Twisted Edwards Form G0 = ( 0x023343e3445b673d38bcba38f25645adb494b1255b1162bb40f41a59f4d4b45e, 0x0c19139cb84c680a6e14116da06056174a0cfa121e6e5c2450f87d64fc000001 ) ``` #### CompressPoint input: `point` output: `compressed_form` 楕円曲線上の点を twisted Edwards form の圧縮形式に変換する. $$ G \leftarrow \mathtt{point} \in E(\mathbb{F}_r) \backslash \{ \mathcal{O}_{E} \} \\ (g_x, g_y) \leftarrow \mathbf{ToTwistedEdwardsForm}(G) \in \mathbb{F}_r \times \mathbb{F}_r \\ \sigma_{x} \leftarrow \mathbf{IsOdd}(g_x) \in \{ 0, 1 \} \\ \mathtt{compressed\_form} \leftarrow \sigma_{x} \cdot 2^{255} + g_y \in \mathbb{N}_{256} $$ #### CurveToHash input: `point` output: `hash` 楕円曲線上の点から、その定義体上のハッシュを得る. $$ G \leftarrow \mathtt{point} \in E(\mathbb{F}_r) \backslash \{ \mathcal{O}_{E} \} \\ (g_x, g_y) \leftarrow \mathbf{ToTwistedEdwardsForm}(G) \in \mathbb{F}_r \times \mathbb{F}_r \\ \mathtt{hash} \leftarrow \mathbf{Poseidon}_{3}(g_x, g_y) \in \mathbb{F}_r $$ #### HashPriv input: `private_key` output: `new_private_key` 秘密鍵をハッシュして新しい秘密鍵を得る. $$ a \leftarrow \mathtt{private\_key} \in \mathbb{F}_{s} \\ a_{1} \leftarrow \mathbf{Sha256}(a) \in \mathbb{B}_{32} \\ a_{2} \leftarrow \mathbf{ToLE}(a_{1}) \in \mathbb{N} \\ a_{2}[31] \leftarrow a_{2}[31] \operatorname{and} \mathtt{0x7F} \\ a_{2}[31] \leftarrow a_{2}[31] \operatorname{xor} \mathtt{0x40} \\ \mathtt{new\_private\_key} \leftarrow \left\lfloor \frac{a_{2}}{8} \right\rfloor \mod s $$ #### PrivToPub input: `private_key` output: `public_key` 秘密鍵から対応する公開鍵を得る. $$ a = \mathbf{HashPriv}(\mathtt{private\_key}) \in \mathbb{F}_s \\ A \leftarrow a \cdot B \\ \mathtt{public\_key} \leftarrow \mathtt{compress\_point}(A) \in \mathbb{B}_{32} $$ #### PubToAddr input: `public_key` output: `address` EdDSA の公開鍵と一意に対応する 20 bytes の文字列(アドレス)を得る. $$ A = \mathbf{DecompressPoint}(\mathtt{public\_key}) \in \langle B \rangle \subset E(\mathbb{F}_r) \\ \mathtt{address} \leftarrow \mathbf{ToLSB}_{160}(\mathbf{CurveToHash}(\mathtt{public\_key})) $$ > 回路内で address を計算するコストが高いので, address の代わりに hashed public key をそのまま使うか, user ID を連番で割り当てることになりそう. #### SignEddsaPoseidon input: `private_key`, `msg` output: `sig` 秘密鍵 `private_key` を用いてメッセージ `msg` に署名する. 後述する `verify_eddsa_poseidon` で検証することができる. $$ m \leftarrow \mathtt{msg} \in \mathbb{F}_{r} \\ a \leftarrow \mathbf{HashPriv}(\mathtt{private\_key}) \\ A \leftarrow \mathbf{PrivToPub}(\mathtt{private\_key}) \\ d \leftarrow \mathbf{Sha256}(a \| m) \in \mathbb{B}_{32} \\ r \leftarrow \mathbf{ToLE}(\mathtt{seed}) \mod s \\ R \leftarrow r \cdot B \\ (R_x, R_y) \leftarrow \mathbf{ToTwistedEdwardsForm}(R) \\ (A_x, A_y) \leftarrow \mathbf{ToTwistedEdwardsForm}(A) \\ h \leftarrow \mathbf{Poseidon}_{6}(R_x, R_y, A_x, A_y, m) \\ \sigma \leftarrow 8ha + d \\ \mathtt{sig} = (R, \sigma) $$ #### VerifyEddsaPoseidon input: `sig`, `public_key` output: `sign_eddsa_poseidon` によって作られた署名 `sig` が `public_key` に対応する秘密鍵を持つアカウントによって作成されたものであることを検証する. $$ (R, \sigma) \leftarrow \mathtt{sig} \\ A \leftarrow \mathtt{public\_key} \\ (R_x, R_y) \leftarrow \mathbf{ToTwistedEdwardsForm}(R) \\ (A_x, A_y) \leftarrow \mathbf{ToTwistedEdwardsForm}(A) \\ h \leftarrow \mathbf{Poseidon}_{6}(R_x, R_y, A_x, A_y, m) \\ \mbox{Check } 8A \neq \mathcal{O}_{E} \\ \mbox{Check } \sigma \cdot B = 8hA + R $$ ### Transaction #### NFT の送信 ID が `token_id` の NFT を `sender_address` が所持している場合、 それを `receiver address` に移動する. `function_signature = 1` (番号は変更の可能性あり) calldata は全部で 96 bytes のバイト列であり, $$ \mathtt{calldata} \leftarrow \mathtt{sender\_address} \| \mathtt{receiver\_address} \| \mathtt{token\_id} \in \mathbb{B}_{96} $$ $$ \mathtt{sender\_address} \in \mathbb{B}_{20}, \\ \mathtt{receiver\_address} \in \mathbb{B}_{20}, \\ \mathtt{token\_id} \in \mathbb{F}_r $$ とする. ただし, 全ての要素を 32 bytes の Big Endian になるように左側 0 padding する. 例えば, `sender_address = 0x86f616f870b89668e6021b0b6070a9234a236c50`, `receiver_address = 0xe7b7e03eb0af47f303cf33707d755cdd9fc22a9f`, `token_id = 1` の場合, `calldata = 0x00000000000000000000000086f616f870b89668e6021b0b6070a9234a236c50000000000000000000000000e7b7e03eb0af47f303cf33707d755cdd9fc22a9f0000000000000000000000000000000000000000000000000000000000000001` となる. #### NFT の発行 `minter` の権限をもつアカウントが自分自身に既出でない ID の NFT を発行する. `function_signature = 2` (番号は変更の可能性あり) calldata は全部で 32 bytes のバイト列であり, $$ \mathtt{calldata} \leftarrow \mathtt{token\_id} \in \mathbb{B}_{32} $$ とする. 例えば, `token_id = 1` の場合, `calldata = 0x0000000000000000000000000000000000000000000000000000000000000001` となる. #### NFT コントラクトのデプロイ (未実装) NFT contract の template 番号 `template_id = 2` を指定して, deployer の address をもとに適切に生成された contract address にデプロイする. ### State Tree Model 全体を俯瞰すると, 次のようになっている. ```mermaid graph TB subgraph world state MT A(0x7713)-- 0 --->B(0x4092) A-- 1 --->BB(0x1727) A-- 2 --->C(0x8335) end C-.-C2 subgraph shared state Layered SMT C2(0x8335)-- 0 --->E(0x2590) C2-- 1 --->G(key: 5, value: 28) E-- 0 --->N(key: 8, value: 92) E-- 1 --->O(key: 10, value: 48) end BB-.-BB2 subgraph CA state SMT BB2(0x1727)-- 0 --->DD(0x7438) DD-- 0 --->HH(address: 4, value: 0x1926) DD-- 1 --->II(address: 6, value: 0x9427) end B-.-B2 subgraph user state SMT B2(0x4092)-- 0 --->D(0x3847) D-- 0 --->H(address: 4, value: 0x1926) D-- 1 --->I(address: 6, value: 0x9427) end II-.-II4 subgraph CA asset SMT II4(0x9427)-- 0 --->QQ(key: 10, value: 4) II4-- 1 --->RR(0x6361) RR-- 0 --->SS(key: 5, value: 73) RR-- 1 --->TT(key: 3, value: 17) end HH-.-HH4 subgraph CA asset SMT HH4(0x1926)-- 0 --->PP(key: 8, value: 17) end I-.-I4 subgraph user asset Layered SMT I4(0x9427)-- 0 --->Q(key: 10, value: 4) I4-- 1 --->R(0x6361) R-- 0 --->S(key: 5, value: 73) R-- 1 --->T(key: 3, value: 17) end H-.-H4 subgraph user asset Layered SMT H4(0x1926)-- 0 --->P(key: 8, value: 17) end ``` <!-- storage と tree との関係 - shared state SMT は Commonly Shared Storage から構成される. - contract asset SMT は CA Asset Storage から構成される. - user asset SMT は User Asset Storage から構成される. --> #### world state MT leaf を 3 つだけもつ Merkle tree. key 0 に対応する値は user state SMT の root hash (in $\mathbb{F}_r$), key 1 に対応する値は CA state SMT の root hash, key 2 に対応する値は shared state SMT の root hash (in $\mathbb{F}_r$) を表す. ```mermaid graph TB subgraph world state MT A(0x7713)-- 0 --->B(0x4092) A-- 1 --->BB(0x1727) A-- 2 --->C(0x8335) end ``` #### shared state SMT Commonly Shared Storage から構成される layered SMT. key は `contract address` と `variable index` の組, value はその contract address と variable index の組に対して格納された値 (in $\mathbb{F}_r$) を表す. ただし, key `(address, 0)` には, 実行コードの template number が格納され, これが 0 でない場合にはそのアドレスはコントラクトと見做される. ```mermaid graph TB subgraph shared state SMT C2(0x8335)-- 0 --->E(0x2590) C2-- 1 --->G(key: 5, value: 28) E-- 0 --->N(key: 8, value: 92) E-- 1 --->O(key: 10, value: 48) end ``` #### contract asset SMT CA Asset Storage から構成される SMT. key は variable index (in $\mathbb{F}_r$), value はそこに格納された値 (in $\mathbb{F}_r$) を表す. #### user state SMT key は user address (in $\mathbb{B}_{20}$), value は user asset SMT の root hash (in $\mathbb{F}_r$) を表す. ```mermaid graph TB subgraph user state SMT B2(0x4092)-- 0 --->D(0x3847) D-- 0 --->H(address: 4, value: 0x1926) D-- 1 --->I(address: 6, value: 0x9427) end ``` #### internal user state MT (仮名称) leaf を 2 つだけもつ Merkle tree. key 0 に対応する値は state diff SMT の root hash (in $\mathbb{F}_r$), key 1 に対応する値は user asset SMT の root hash (in $\mathbb{F}_r$) を表す. ```mermaid graph TB subgraph internal user state MT I1(0x9427)-- 0 --->I2(0x8218) I1-- 1 --->I3(0x4573) end ``` #### user asset SMT User Asset Storage から構成される layered SMT. key は `contract address` と `variable index` の組, value はその contract address と variable index の組に対して格納された値 (in $\mathbb{F}_r$) を表す. この state tree は user が保管し, トランザクションを送信したり L2 から exit する際に必要になる. ```mermaid graph TB subgraph user asset SMT I4(0x4573)-- 0 --->Q(key: 10, value: 4) I4-- 1 --->R(0x6361) R-- 0 --->S(key: 5, value: 73) R-- 1 --->T(key: 3, value: 17) end ``` #### state diff SMT key は nonce (in $\mathbb{N}_{64}$), value は nonce に対応する user asset diff SMT の root hash (in $\mathbb{F}_r$) を表す. ```mermaid graph TB subgraph state diff SMT I5(0xc932)-- 0 --->I6(nonce: 0, value: 0x8218) end ``` #### user asset diff SMT 各トランザクションで発生する user account の asset 変動のみを格納した SMT. key は contract address と variable index の Poseidon hash (in $\mathbb{F}_r$), value はその contract address と variable index の組に対して格納された値の変化量 (in $\mathbb{F}_r$) を表す. ```mermaid graph TB subgraph user asset diff SMT U(0x8218)-- 0 --->V(key: 10, diff: +1) U-- 1 --->W(0x1414) W-- 0 --->X(key: 5, diff: -20) W-- 1 --->Y(key: 3, diff: +10) end ``` #### shared state diff SMT トランザクションで発生する shared state の変化のみを格納した SMT. 構造は user asset diff SMT と同様. #### contract asset diff SMT トランザクションで発生する contract account の asset 変動のみを格納した SMT. 構造は user asset diff SMT と同様. ## Comments last block はどこに書く? 差分は state tree の中には<s>いらない. user asset はマージした状態で渡し, なぜちゃんとマージできているかを確認するには ZKP を使う.</s>いる. 最後の tx が取り込まれたことを確認する方法として, address は user ID にする (登録制), その時の user state root と id list (user ID と last block) の inclusion proof. onchain cost を減らすための ID. NFT を受け取るためには - online にいる (この場合, 手数料を安く利用できるようにする) - agent が代わりに受け取る - blob に書き込む のいずれかをしなければならない. どのように受け取ったことを認知するか? -> やっぱり approve, transferFrom 形式の方が良くない? 状態の同期が取れてないのに tx を実行してしまう可能性がある. -> CA storage の内容は L1 で常に検証されなければならない. 特定の人が管理している訳ではないので L2 が停止した時に取り出せなくなってしまう. 記録されていれば, L2 を再起動することもできる. -> 要は, approve, transferFrom 形式だと allowance は CA storage におかなければならず, calldata 使用量が増える. -> 増加差分は公開しておくので, オンラインに復帰したら早めにマージしてね. ## Reference [1] https://eips.ethereum.org/EIPS/eip-2494 [2] https://ed25519.cr.yp.to/ed25519-20110926.pdf ###### tags: ``