# IX. Service Accounts > [name=Eugene] > ==TODO: 移動至 Note 4. Overview== > 前情提要:建議先閱讀 4.9 The Core Model and services > Ethereum 是使用 *on-chain consensus*, 所有節點都需要對所有的交易進行計算,確保網絡的狀態一致,但效能較差,系統缺乏擴展性。 > JAM 提出一種新的方式 *in-core consensus*, 只有部份的節點進行計算,但由於共識不是所有節點進行驗證,因此透過 *guaranteeing*, *assuring*, *auditing* 以及有可能需要 *judging*, 來讓這個由部份節點所計算結果足夠被相信是有效的。 > 這些在 in-core 計算的結果, 都必須可以在已最終確定的區塊上重新執行,也拿到相同的結果, 將 in-core 設計為 stateless, 將不需要考同步性,並且可以很容易的將計算重現於任何區塊 (只要使用相同的輸入參數即可), 而為了達到這個需求,in-core 的執行需要以下內容: > - the refinement code (執行邏輯, 處理輸入、產生結果) > - the code of the authorizer (驗證這一個 in-core 執行是合法的) > - preimage lookups (可以在執行期間查詢到相關資料) JAM 中的 service 類似於 Ethereum 的智能合約 (smart contract), Service Accounts 包含以下三個部份 - Code (程式碼) - Storage (儲存空間) - Balance (餘額) 相較於 Ethereum, JAM 把程式碼邏輯分為兩個部份: - Refinement - Stateless - in-core (高效能的計算) - Accumulation - Stateful (依賴於前面的區塊參數) - on-chain (涉及狀態更新與儲存) --- Service Accounts 被儲存於狀態 $\sigma$ (sigma) 中, 記錄為 $\delta$ (delta) 可以使用服務辨識符 (service identifier) $\mathbb{N}_S$ 來映射到一個描述 service 的 $\mathbb{A}$ tuple (包含了數個描述 service 的資訊) $$ \mathbb{N}_S \equiv \mathbb{N}_{2^{32}} $$ $$ \delta \in \mathbb{D} \langle {\mathbb{N}_S} \to {\mathbb{A}} \rangle $$ - $\mathbb{N}_S$ 是 service identifier (index) - $\mathbb{N}_{2^{32}}$ 是一個 32 位的自然數,表示最多可以定義 $2^{32}$ 個 service account - $\mathbb{A}$: The set of service accounts - $\mathbb{D}$: The set of dictionaries > State $\sigma$ 所儲存的資訊可以參考 4.2. The State. --- 以下定義為一個 service account $\mathbb{A}$ 中所包含的資訊 ![image](https://hackmd.io/_uploads/rkMshcWmJl.png) - $\mathbf{s}$: storage dictionary - $\mathbf{p}$: preimage lookup dictionary - $\mathbf{l}$: preimage lookup dictionary - $c$: code hash - $b$: balance - $g$: gas (執行 accumulate 所需的最低 gas) - $m$: gas (執行 on transfer 所需的最低 gas) > - $\mathbb{D}$: The set of dictionaries > - $\mathbb{Y}$: The set of octet strings/blobs > - $\mathbb{H}$: The set of 32-octet cryptographic values (沒有下標的 $\mathbb{H}$ 通常代表 hash function 的結果) > - $\mathbb{N}_B$: The set of balance values. Equivalent to $\mathbb{N_{2_{64}}}$. See equation 4.21. > - $\mathbb{N}_G$: The set of unsigned gas values. Equivalent to $\mathbb{N_{2_{64}}}$. See equation 4.23. > - $\mathbb{N}_L$: The set of blob length value. Euqivalent to $\mathbb{N}_{2^{32}}$. See equation 3.4. > - $\mathbb{N}_T$: The set of timeslot values. Equivalent to $\mathbb{N}_{2^{32}}$. See equation 4.28. 因此,你可以透過 service identifier 來取得在 $\mathbb{A}$ 中的相關資訊,例如以下範例: - $\delta[\mathbf{s}]_b$: 取得 service account $s$ 的 balance - $\delta[s]_\mathbf{s}[k]$: 取得一個在 service account $s$ 的 storage $\mathbf{s}$ 中,以 $k$ ($k \in \mathbb{H}$) 作為 key 的儲存項目 > :robot_face: > $\delta[\mathbf{s}]_b$:`service_accounts[service_index].balance` > $\delta[s]_\mathbf{s}[k]$:`service_accounts[service_index].storage[storage_key]` --- ## 9.1 Code and Gas - Code $\mathbf{c}$ 在 service account 中會以 hash 進行表示 - 如果一個 service 是可以執行的 (具有 code 資訊), 那麼 code $\mathbf{c}$ 就必須存在於他自己的 preimage lookup 中 (See 9.2 Preimage Lookups) 對於所有屬於 $\mathbb{A}$ 的 service account $\mathbf{a}$, 其 code $\mathbf{c}$ 可以被定義為: ![9.4](https://hackmd.io/_uploads/BJvudRgQkg.png) ![v0.6.3 (9.4)](https://hackmd.io/_uploads/B12CYzQs1l.png) > [name=yu2C] 多出 metadata 讓 fetch 的時候可以由第一個 byte 就知道這些額外 info > > - name, author, version, licence/copyright... > - 左邊為 pre-iamge, 右邊用 code hash 拿到相對應的 pre-image > - $a_\mathbf{m}$ 只是 accumulation `fetch` 的時候用而已 :::info - $\mathbf{a}_{\mathbf{c}}$: (**dictionary value**) 儲存於 preimage 中 service account 的 code - $\mathbf{a}_c$: (**dictionary key**) 儲存於 service account 的 code hash ```python # 公式想要表達的意思:從 Dictionary 中取值, default None code = preimage_dict.get(code_hash, None) ``` ::: --- Code 會有三種進入點: - **0 refine**: Refinement, executed in-core and stateless. - **1 accumulate**: Accumulation, executed on-chain and stateful. - **2 on_transfer**: Transfer handler, executed on-chain and stateful. > 關於 Refine 如何在 in-core 進行計算, 會在 section 14.3 Packages and Items 有更詳細的介紹 --- ### 關於 Gas - JAM 虛擬機中的執行時間,以 *gas* 為單位進行測量 - 可以被表示為小於 $2^{64}$ 的自然數, 被寫為 $\mathbb{N}_G$ - 如果數值可能負值,可以使用 $\mathbb{Z}_G$ 來表示 $\mathbb{Z}_{-2^{63}...2^{63}}$ 每個 service account 定義了兩種 *gas* 限制: - $g$: 執行 *Accumulate* 所需的最低 *gas* - $m$: 執行 *On Transfer* 所需的最低 *gas* ## 9.2 Preimage Lookups 以下為 preimage lookup dictionary 與 storage 的比較表 | 特性 | Preimage Lookups | Storage | | --- | --- | --- | | **映射類型** | 從 hash 映射到 preimage | 任意鍵(Key)映射到值(Value) | | **資料來源** | 資料由外部(extrinsically)提供 | 資料來自服務的內部累積(accumulation) | | **移除機制** | 需先標記為不可用 (unavailable),經過一段時間後才能移除 | 可自由移除 | | **歷史資訊保存** | 保留 preimage 歷史, 提供查詢歷史可用性 | 不會保留歷史資訊 | | **查詢目的** | 設計為核心(in-core)查詢,供服務代碼的 Refine 邏輯使用 | 通常僅供一般存儲需求 | Accumulate 與 Refine 的差別: - Accumulate 階段 (on-chain),驗證者可以透過 State $\sigma$ 中得知相關的靜態資料 - Refine 階段 (in-core), 驗證者無法取得 State $\sigma$ 進行使用, 因此將一個歷史狀態稱做 `lookup anchor` (必須要是最終確定的狀態), 作為一個參考歷史點,讓 Refine 階段的計算結果,也可以正確的累積 (accmulate) 透過保留可用的歷史資料,我們相信任何在最近最終確定範圍內的驗證者,都能夠確定任意的 preimage 是可以在審核期間被存取到 -> 任何一個驗證者,只要他們擁有最近完成最終確認的 view (他們有權限的話), 就能判斷某個特定的 preimage 在審核期間內是否曾經被使用 ### *historical lookup function* $\Lambda$ (Lambda) 歷史查詢函數,可以查詢到某一個 service account $\mathbf{a}$ 在某一個時間點 $t$,是否可以使用這一個 preimage hash $h$ 查詢到對應的 preimage > 以下公式只是簡短的定義, 後續會有詳細版公式 ![image](https://hackmd.io/_uploads/B1fQDfg7Jg.png) ![v0.6.5 (9.5)](https://hackmd.io/_uploads/S1_-z4Syxg.png) - $\mathbf{a}$: 一個 service account - $t$: 查詢的時間點 - $\mathcal{H}_{(\mathbf{p})}$: preimage 的 hash value - $\mathcal{H}$: The Blake 2b 256-bit hash function. See section 3.8. - $v$: historical lookup function 的輸出結果 - account $\mathbf{a}$ 可以使用,拿回該 preimage $\mathbf{p}$ - 或是 account $\mathbf{a}$ 不可使用,拿到空值 $\varnothing$ > - $\mathbb{A}$: The set of service accounts > - $\mathbb{N}_{\mathbf{H}_t - C_D...\mathbf{H}_t}$: 一個時間點 (==$C_D$ 找不到定義的地方==) > - 這邊想要表達從歷史到當前的 time slot index, 可以作為輸入的時間點 > - $\mathsf{D}= 19, 200$: The period in timeslots after which an unreferenced preimage may be expunged. > - $\mathbb{H}$: The set of 32-octet cryptographic values. > - $\mathbb{Y}$: The set of octet strings/blobs - $\delta[s]_\mathbf{p}$: 代表該 service 的 preimage dictionary, 可以透過 preimage hash 查詢 preimage - $\delta[s]_\mathbf{l}$: 代表該 service 可用的歷史資料,可以透過 hash 以及資料長度作為 dictionary key 進行查詢 ### 9.2.1 Invariants lookup 系統應該具有一些不變特性 ![image](https://hackmd.io/_uploads/ry0yZQlX1g.png) $\forall a \in \mathbb{A}, (h \mapsto \mathbf{p}) \in a_{\mathbf{p}}$ : 所有在 service accounts 集合中的 account, preimage dictionary 中存在的 hash 映射 preimage,必須符合以下條件: 1. $h = \mathcal{H}({\mathbf{p}})$ : hash 必須是 preimage hash 的結果 2. $(h, |\mathbf{p}|) \in \mathcal{K}(a_\mathbf{l})$ : hash 以及 preimage 的長度,必須是 account $l$ dictionary 中的其中一個 key > - $\mathcal{K}(a_{\mathbf{l}})$: 是一個 service account 儲存 preimage 歷史資訊的 Dictionary keys > - $\mathcal{K}$: 是一個可以拿回 Dictionary keys 的函數 > - $(h, |\mathbf{p}|$): 一個由 preimage hash 以及 preimage length 所組合成的 tuple > - $\mathbb{H}$: The set of 32-octet cryptographic values (沒有下標的 $\mathbb{H}$ 通常代表 hash function 的結果) ### 9.2.2 Semantics $h \in [\![\mathbb{N}_T]\!]_{:3}$ - $h$: 歷史狀態 (historical status component) - $[\mathbb{N}_T]_{:3}$: 最多可以儲存三個時間點(time slot)的序列 這一個歷史狀態 $h$ 序列的長度,會有四種的狀態模式: - $h = []$ - 表示已經請求 preimage 中, 但是還沒被提供 (還沒被儲存) - $h \in [\![\mathbb{N}_T]\!]_1$ - 表示從 $h_0$ 這個時間點開始, preimage 可以被存取 - $h \in [\![\mathbb{N}_T]\!]_2$ - 表示之前 ($h_0$) 可以使用的 preimage 自從 $h_1$ 後不可以被存取了 (preimage $h_0$ 到 $h_1$ 之間是可以使用的) - $h \in [\![\mathbb{N}_T]\!]_3$ - 表示 preiamge 在 $h_0$ 到 $h_1$ 的時間內可以使用,但是後來無法存取,直到 $h_2$ 後,再次可以被存取。 透過以上的定義,historical lookup function $\Lambda$ (Lambda) 可以被定義為以下: ![image](https://hackmd.io/_uploads/Bk_5y3eXJx.png) ==h 如何更新, 如果更多歷史出現== :::info **historical lookup funciton 參數** $\Lambda(\mathbf{a}, t, h)$ 是一個歷史查詢函數,需要以下三種輸入參數: - $\mathbf{a}$: 一個 service account - $t$: 想要查詢的時間點 - $h$: preimage hash ::: :::info **historical lookup funciton 邏輯** 若符合以下兩個條件, 代表 account 能夠在 $t$ 時間點使用該 preimage - $h \in \mathcal{K}(\mathbf{a}_{\mathbf{p}})$ - hash 屬於 account preimage dictionary 的 keys 之一 - $I (\mathbf{a}_{\mathbf{l}}[h, |\mathbf{a}_{\mathbf{p}}[h]|], t)$ - $I$: 是一個函數,可以判斷 account $\mathbf{a}$ 在某個時間點 $t$ 是否可以取用 preimage $\mathbf{p}$ - $\mathbf{a}_{\mathbf{l}}[h, |\mathbf{a}_{\mathbf{p}}[h]|]$ - $\mathbf{a}_{\mathbf{l}}$ : account 儲存 preimage 歷史資訊的 dictionary - 該 dictionary 的 key 是由 preimage hash 及 preimage length 所組成 - $\mathbf{a_{\mathbf{p}}}$ : account 的 preimage dictionary - $h$ : preimage hash - $t$ : 查詢時間點 - 回傳 boolean: True or False - 在這個時間點,該 service account 是否可以存取該 preimage - $I$ 函數回傳 boolean,取決於上面描述的四種狀態模式 (不同時間點該 preimage 是否可以被使用) ::: :::info **historical lookup function 輸出** 如果 account $a$ 可以在 $t$ 時間點取用以 preimage hash $h$ 所拿到的 preimage $\mathbf{p}$ 那麼將會取得該 preimage, 否則拿到 $\varnothing$ ::: :::spoiler historical lookup function - pseudo code ```python def historical_lookup_function(account, time, hash): if hash_is_preimage_dict_key and is_available_time: return account.preimage[hash] else: return None def hash_is_preimage_dict_key(hash, preimage_dict): if hash in list(preimage_dict.keys()) return True return False def is_available_time(history, lookup_time): if len(history) == 0: return False if len(history) == 1: x = history[0] return x <= t if len(history) == 2: x = history[0] y = history[1] return x <= t < y if len(history) == 3: x = history[0] y = history[1] z = history[2] return (x <= t < y) || z <= t ``` ::: ### 9.3. Account Footprint and Threshold Balance 只要 service's storage 有所改變,這兩個變數都需要被更新 - $i$: the number of items in storage (儲存項目的數量) - $l$: the total number of octets used in storage (octets 儲存的總大小) > 這兩個資訊在 serialization function 也會用到 - $t$: 根據該 service account 的儲存項目與空間,所需要的最低餘額數量 (儲存東西要付錢) ![image](https://hackmd.io/_uploads/HJyhM0lX1l.png) ![GP v0.5.4 (9.8)](https://hackmd.io/_uploads/rJE5bkfPyg.png) - $\forall a \in \mathcal{V}(\delta)$: 所有的 service account 都會屬於 service accounts dictionary $\delta$ 的 value 之一 :::info #### $a_i$ (儲存項目的數量) $$ a_i \in \mathbb{N}_{2^{32}} \equiv 2 \cdot |a_\mathbf{l}| + |a_\mathbf{s}| $$ - 是一個 $2^{32}$ 自然數 - $|a_{\mathbf{l}}|$: service account metadata (historical information) dictionary 的大小 - $|a_{\mathbf{s}}|$: service account storage dictionary 的大小 - ==為什麼乘 2? -> 因為 dictionary 包含 key 以及 value== > [name=yu2C] 因為 $a_{\mathbf{l}}$ 的 key size 比別人大兩倍? ::: ::: info #### $a_l$ > octets 儲存的總大小 (storage diciontary $s$ + preimage lookup dictionary $l$) $$ a_l \in \mathbb{N}_{2^{64}} \equiv \sum\limits_{(h,z) \in \mathcal{K}(a_\mathbf{l})} (81 + \mathcal{z}) + \sum\limits_{x \in \mathcal{V}(a_s)} (32 + |\mathcal{x}|) $$ - 是一個 $2^{64}$ 自然數 - $(h, z) \in \mathcal{K}(a_\mathbf{l})$ - $\mathcal{K}(a_\mathbf{l})$: service account metadata dictionary 的 keys - $(h, z)$: metadata dictionary key 是一個 tuple - $h$: preimage hash - $z$: preimage 的長度 ($|\mathbf{p}|$) - ==81 是? hash 固定大小?== > [name=yu2C] How many bytes we are used to store items(keys) - $x \in \mathcal{V}(a_s)$ - $\mathcal{V}(a_s)$: service account storage dictionary 的 values - $x$: service account storage 某個儲存的數值 - ==32 是?== size of hash ::: :::info #### $a_t$ > service account 必須持有的最低餘額門檻, 因為他使用了這些儲存空間 > (基礎費用 + items 數量計費 + octet 儲存空間計費) $$ a_t \in \mathbb{N}_B \equiv \mathsf{B}_S + \mathsf{B}_I \cdot a_i + \mathsf{B}_L \cdot a_l $$ - $\mathbb{N}_B$: The set of balance values, 是 $2^{64}$ 的自然數 - (constant) $\mathsf{B}_S$ = 100, 所有 services 所需要的最低餘額 - (constant) $\mathsf{B}_I$ = 10, 儲存每個 item 所需要的最低餘額 - (constant) $\mathsf{B}_L$ = 1, 儲存每個 octet 所需要的最低餘額 ::: ## 9.4 Service Privileges. - 最多可以有三個 service 可以成為具有特權的 service - 某些服務可以對特定的狀態進行變更,或是自動累積資源 - 這是 state 的一部分,被表示為 $\chi$ :::info Service Privileges $\chi$ 是由三個 service index 以及 gas 的限制所組成 $$ \chi \equiv ({ {\chi_m} \in {\mathbb{N}_S}, {\chi_a} \in {\mathbb{N}_S}, {\chi_v} \in {\mathbb{N}_S}, {\chi_\mathbf{g}} \in {\mathbb{D} \langle {\mathbb{N}_S} \rightarrow {\mathbb{N}_G} \rangle} } ) $$ - $\chi_m$: 代表一個 *manager* service 的 index, 這個 service 能夠影響區塊之間 $\chi$ 的組成 (可以影響 $\chi$ 中的 service index 或是 gas limit 數值) - $\chi_a$: 可以修改區塊之間的 $\varphi$ (authorization queue) - $\chi_v$: 可以修改區塊之間的 $\iota$ (pending validators set) - $\chi_\mathbf{g}$: 是一個 Dictionary, 會持續累積每一個 service index, 在每一區塊所累積的基本 gas 數量 (每一個 service index 自動獲得基本的 gas 數量) :::