# Appendix A, B PVM Related <!--## 4.7 Gas 部分 ![Eq. 4.22](https://hackmd.io/_uploads/SJe178VVkx.png) 在 input 部分的 gas 是正的 $\mathbb{N}_G$,在 output,gas 可能是負的,表示嘗試執行超過 gas limit。--> ## PVM Input 參數介紹 在 JAM 的 Polka Virtual Machine(PVM)中,執行一次程式需要五個核心輸入參數。這些參數共同描述了「程式本體」與「執行狀態」,確保驗證者能以決定性方式重現結果。 --- ### 1. `p`:Program blob(程式碼) 一串 **位元組序列(byte sequence)**,代表整個程式的機器碼。 結構上由三個部分組成: - **`c`** → instruction data(原始程式位元組流) - **`k`** → opcode bitmask(哪些位元組是指令起點) - **`j`** → dynamic jump table(動態跳轉目標對照表) **範例** ``` p = [0x40, 0x05, 0x50] k = [1, 0, 1] ``` 代表一段程式: - 第 0 位 (0x40) = 第一條指令起點,後面跟參數(imm) 0x05 - 第 2 位 (0x50) = 第二條指令 執行時 PC 會從 0 → 2 → 結束。 ## 2. `pc`:Program Counter(程式計數器) - 指向程式目前執行位置,單位是 **位元組索引**。 - 執行後: - 普通情況 → `pc` 移到下一條指令的起點(靠 `skip()` 計算)。 - 若遇到 branch / jump → `pc` 會直接更新成目標位址,必須是合法起點,否則 **panic**。 --- ## 3. `gas`:Gas(剩餘可用資源) - 執行「計步器」,避免無窮迴圈或惡意消耗。 - 每條指令都會消耗固定或類別相關的 gas。 - 若資源不足 → VM 立即以 **out-of-gas (∞)** 結束。 **範例** - 初始 `gas = 3`,程式有三條指令 → 每執行一次就扣 1 → gas=0 後停止。 --- ## 4. `regs`:Registers(暫存器組) - 一組 **13 個暫存器**,用來存放中間運算值。 - 用途:算術運算、條件比較、跳轉判斷等。 - 指令可能會讀寫不同暫存器,並涉及帶號/無號詮釋與位元長度轉換。 **暫存器名稱** ```go var RegName = [13]string{ "ra", "sp", "t0", "t1", "t2", "s0", "s1", "a0", "a1", "a2", "a3", "a4", "a5", } ``` ``` regs = [0,0,0, ...,0] # 初始 13 個暫存器 指令: load_imm a0, 5 寫入值 → 執行後 regs[7] = 5 ``` ### 5. ram:RAM(記憶體) 執行期間的記憶體,支援讀/寫,並有存取控制。 特殊規則: - 存取 小於 2^16 的非法位址 → panic (⭍)。 - 存取 大於等於 2^16 的非法位址 → page-fault (Ⅎ),交由 host 決定是否補頁。 - 沒有寫入權限 → 報錯並回傳失敗位址。 PVM 能精確區分 panic / page-fault,保證決定性。 若程式執行 store 100, a0,但位址 100 不在可寫範圍 → panic。 若執行 load 70000, a1,而該高位址頁尚未載入 → page-fault。 ## A.1. Basic Definition 在 PVM 中,程式的執行由一個通用函式 **general PVM function** $\Psi$ 來定義。 它的核心觀念是將程式碼與狀態輸入進虛擬機,並透過「單步狀態轉換」不斷推進,直到遇到結束條件。 ### 函式定義 :::info input - $\mathbf{p}\in\mathbb{Y}$ : program code in blob - $\imath\in\mathbb{N}_R$ : program counter - $\varrho\in\mathbb{N}_G$ : gas - $\omega\in[\mathbb{N}_R]_{13}$ : register - $\mu\in\mathbb{M}$ : $\small\text{RAM}$ ![image](https://hackmd.io/_uploads/r1S45qmdyx.png) output - $\hbar$ : host call - $\blacktriangleright$ : start the exit reason $\varepsilon\in\{\blacksquare, ⭍, \infty\}\cup\{\Finv, \hbar\} \times \mathbb{N}_{R}$ - $\blacksquare$ : regular halt - $⭍$ : panic - $\infty$ : out-of-gas - $\mathbf{\Finv}$ : page-fault (嘗試存取 RAM address 失敗) - $\hbar$ : host-call - 更新後的狀態 $(p', \imath', \varrho', \omega', \mu')$ ::: PVM 的全域執行過程可視為: 1. 呼叫 $\Psi$ 進行單步轉換。 2. 若結果為 **continue** → 更新狀態並進入下一輪。 3. 若結果為 **exit reason** → 停止,並回傳結束原因。 PVM 的核心是「單步轉換函式」$\Psi_1$,全域的 $\Psi$ 只是將它不斷重複。 不需要事先分辨 panic / page-fault 的細節,因為最終都會以 $\varepsilon$ 表示退出原因。 ![v0.6.2 (A.1)](https://hackmd.io/_uploads/BkBoHQMKkg.png) ![v0.6.5 (A.1)](https://hackmd.io/_uploads/H1vfUErJel.png) ```rust pub fn generalPVM(p, pc, gas, register, ram) -> Result { /*TODO: calc p dependencies c, k, j*/ Result = single_step_invocation(c, k, j, pc, gas, register, ram).ok_or(Err()) // remind to handle error in ok_or(Err()) } ``` ![v0.6.2 (A.2)](https://hackmd.io/_uploads/rkuurQfK1x.png) :::info $\nabla$ : (給 core 看的) error 存在唯一映射 $\mathbf{p}\to(\mathbf{c}, \mathbf{k}, \mathbf{j})$ ::: ## A.2. Instruction, Opcodes and Skip-distance 在 PVM 中,程式碼本體 `p` 是一串 **位元組序列 (byte sequence)**。 然而,虛擬機並不是直接解碼,而是透過先預處理 code 找出 **指令邊界 (opcode bitmask)** 與 **跳轉表 (jump table)** 來決定哪些是「指令起點」、哪些只是參數。 完整的 program blob 會被拆解為: - **$c$** → instruction data:原始位元組流 - **$k$** → opcode bitmask:位元陣列,$k[i] = 1$ 表示第 $i$ 位元組是指令起點 - **$j$** → dynamic jump table:用於動態跳轉指令的目標查詢 因為指令長度可變(最長 16 bytes),PVM 需要一個 `skip()` 函數來找到下一條指令的起點。 - 指令的「下一個位置」為: ``` [pc_{next} = i + 1 + skip(i)] ``` Notes: 1. PVM 程式碼是以「byte」為單位計數,不是「第幾條指令」。 2. `k` 用來定義哪些位元組是指令邊界。 3. `skip()` 函數決定了 PC 如何移動到下一條指令。 4. 所有跳轉必須落在合法的 basic block 起點,否則直接 panic。 ![image](https://hackmd.io/_uploads/S1F9fi7_yx.png) :::info - $\mathbf{c}, \mathbf{k}$ 定義了一組指令序列 - 進一步組成了 $basic$-$block~sequence$ : indice 搜尋集, 會對應到 $block$-$termination$ 之後的指令 - $\mathbf{j}$ 是指令數據塊的一組索引集合,當執行動態計算的跳轉時會被索引 - 每一項以相同的 octect 長度($z$) 被 encoding 成自然數序列 - 長度為 $z$ - 會先被被單獨 encoding,然後再用來 encoding $\mathbf{j}$ PVM 計算 octet 的數量而不是指令的數量 - 因此需要明確定義哪些 octect 代表指令的起始位置(opcode octet),哪些不是 - 這就是 $\mathbf{k}$ ( instruction-opcode bitmask)的用途。 - bitmask 的長度 == 指令 blob 的長度 ::: ![v0.5.4 (A.2)](https://hackmd.io/_uploads/rJ_MMjIwyg.png) (**指令表用到的 basic func**) 定義 Skip function 跳過目前指令 $i$ - ( $\mathbf{k}=0$ 跳到下一個 $\mathbf{k}=1$ ) :::info $\mathbb{N}\to\mathbb{N}$ : 目前指令的 index -> 距離下個指令的距離 - 目前指令 $i$ - $\min{(24, j)}$ : 限制跳躍的最大值(距離)為 24 - $j\in\mathbb{N}$ : 距離 - $(\mathbf{k}\smallfrown[1,1,...])_{i+1+j}=1$ : 從 $i$ 往後搜索 $j$ 找到 $\mathbf{k}=1$ 的位置 - $\mathbf{k}$ 為 bitmask - 1 代表指令開頭 - 0 代表不是(你在指令中) - $\mathbf{k}\smallfrown[1,1,...]$ : concat 無限長的序列 - 確保長度超過 bitmask $\mathbf{k}$ 的長度也可以運作 - 最後一個指令是 $\mathrm{skip}(|\mathbf{c}|-1)$ - 給定 index $i$, 其操作碼為 $\mathbf{c}_i$, 到下一個指令的距離 (octets) 為 $1+\mathrm{skip}(i)$ - $1$ : bitmask - 每條指令的長度隱含為 16 個 octets - $\mathrm{len}(\mathbf{c})\le 16~ octets$ ::: ![v0.5.4 (A.3)](https://hackmd.io/_uploads/HJB7PuDP1g.png) :::info (**指令表用到的 basic func**) 定義 $\zeta$ 等價於 $\mathbf{c}$, 為了實作上避免 out-of-bounds 的操作, 所以 cancat 無限長的 $[0]$ 序列來製造 **trap** ::: ## A.3. Basic Blocks and Termination Instrucments 在 PVM 中,**Basic Block(基本區塊)** 是程式碼的基本單位。 它定義為一段「直線執行」的指令序列: - 只有一個 **入口**(第一條指令)。 - 只有一個 **出口**(最後一條指令)。 - 中間沒有跳轉或分支。 ### Basic Block 的組成 - **起點**:必須是合法的指令開頭(由 bitmask `k` 決定,`k[i] = 1`)。 - **終點**:必須是 **termination instruction(終止指令)**。 若跳轉目標不是合法起點,或 basic block 沒有以終止指令結尾 → 觸發 **panic**。 ### Termination Instructions 分類 1. **Trap 與 Fallthrough** - `trap` → 立即結束該 block,產生錯誤或停止。 - `fallthrough` → 繼續到下一條指令(常作為安全結尾)。 2. **Jumps** - `jump` → 靜態跳轉到固定位址。 - `jump_ind` → 動態跳轉,目標由跳轉表 `j` 決定。 3. **Load-and-Jumps** - `load_imm_jump` → 載入立即數後跳轉。 - `load_imm_jump_ind` → 載入立即數後再查 `j` 動態跳轉。 4. **Branches** - 依條件比較結果跳轉: - `branch_eq`, `branch_ne` - `branch_ge_u`, `branch_ge_s`, `branch_lt_u`, `branch_lt_s` - 帶立即數的比較: - `branch_eq_imm`, `branch_ne_imm` - `branch_lt_u_imm`, `branch_lt_s_imm`, `branch_ge_u_imm`, `branch_ge_s_imm` Example ``` p = [0x10, 0x40, 0x05, 0x20, 0x60, 0x70, 0x30, 0xFF] ``` Note: - 0x10 → 普通單 byte 指令 - 0x40 0x05 → 帶立即數的指令(0x40 為指令,0x05 為參數) - 0x20 → 單 byte 指令 - 0x60 0x70 → jump 6(跳到位址 6) - 0x30 → 單 byte 指令 - 0xFF → trap(終止指令) ``` k = [1, 1, 0, 1, 1, 0, 1, 1] ``` ### Basic Blocks 劃分 1. Block 1: - 指令:0x10 → 0x40 0x05 → 0x20 → 0x60 0x70 (jump 6) - 結尾是 jump → Block 1 結束 2. Block 2(跳轉目標) - 指令:0x30 → 0xFF (trap) - trap 是終止指令 → Block 2 結束 ### 執行流程 PC=0 → 執行 Block 1: - 0x10 (普通指令) - 0x40 0x05 (load imm 5) - 0x20 (普通指令) - 0x60 0x70 (jump 6) → 跳轉到位址 6 PC=6 → 執行 Block 2: - 0x30 (普通指令) - 0xFF (trap) → 程式結束 ### Note 1. **Basic Block = 直線程式片段**,只能有一個出口。 2. **結尾必須是終止指令**,不允許落在未定義位置。 3. **跳轉目標必須是合法的 block 起點**,否則 panic。 ![v0.5.4 (A.4)](https://hackmd.io/_uploads/SJ1rsYDPkx.png) ==有點像是區塊開始的指令碼...0, 指令1, 指令2,...== 定義指令操作碼 (instruction opcode indices) 的 index $\varpi$, 為基本塊的開始,將終止指令的 index 一一列出來 :::info - $T$ 為操作碼 (opcode) 的 index, 而不是名稱 - 三個 condition - 從 $\mathbb{N}_{|\mathbf{c}|}$ 依序拿出來為 $n$ - bitmask $\mathbf{k}$ 的第 $n$ 個為 1 - 代表指令的開頭 - 操作碼 $\mathbf{c}$ 的第 $n$ 個要屬於 $T$ - $[n+1+\mathrm{skip}(n)]$ 下一個指令的 index ::: ## A.4. Single-Step State Transition single-step PVM state-transition function $\Psi_1$ ![v0.5.4 (A.5)](https://hackmd.io/_uploads/HJvfsFvPyg.png) 在 PVM 中,**單步狀態轉換 (single-step invocation)** 是核心邏輯。 每次呼叫會取出一條指令,更新執行狀態,或返回一個結束原因。 全域的執行函式 $\Psi$ 只是將這個單步函式 $\Psi_1$ 重複呼叫,直到退出。 ### 狀態定義 一個 PVM 狀態表示為: $c$ → instruction data(從 p 直接得到的位元組流) $k$ → opcode bitmask(哪些位元組是合法起點) $j$ → dynamic jump table(動態跳轉表) $\imath$ → program counter (PC),位元組索引 $\varrho$ → gas(剩餘資源) $\omega$ → 暫存器組(長度 13 的整數陣列) $\mu$ → RAM(記憶體狀態) 輸出包含: 新的狀態 exit reason $\varepsilon$ → exit reason 可能是 $\blacksquare$ (halt), $\dashv$ (panic), $\infty$ (out-of-gas), $\mathbb{F}$ (page-fault), $\hbar$ (host call),或 $\triangleright$ (繼續執行) $\imath'$ → 更新後的 PC $\varrho'$ → 更新後的 gas $\omega'$ → 更新後的暫存器 $\mu'$ → 更新後的記憶體 ![v0.5.4 (A.6)](https://hackmd.io/_uploads/By5j6YDwkx.png) :::info 在虛擬機執行指令的單步狀態轉換 $\Psi_1$ 過程中,通常會滿足以下條件: - 機器不會停止執行 - 指令計數器 $\imath$ 增加 $1+\mathrm{skip}(\imath)$ - 跳到下一個指令 - 剩餘的 gas 減少,減少的量 $\varrho\small\Delta$ 取決於指令類型 - 記憶體 $\small\mathrm{RAM}$ $\mu$ 和暫存器 registers $\omega$ 保持不變,除非指令特別指定了改變。 其他具體指令的定義往往是針對以下規則的例外情況 ::: ```rust pub fn single_step_invocation(c, k, j, pc, gas, register, ram) -> Result { match exitcond == pass { /*TODO: do STF*/ } /* TODO: Other condition - ram error - */ } ``` ### 例外: 記憶體訪問錯誤 ![v0.5.4 memory R problem](https://hackmd.io/_uploads/H1tQCvKv1l.png) ![v0.5.4 memory W problem](https://hackmd.io/_uploads/rJLq1dKvke.png) 在執行指令的過程中,可能會訪問 RAM(隨機存取記憶體)。 - 當所需的 RAM 索引低於 2162^{16} 2 16(即 65536)時, - 無論該索引的值是否可存取,機器都會立即發生「恐慌」(panic),並且不會對其狀態做出任何進一步的更改。 - 如果所請求的 RAM 索引超過該範圍,但該索引不可存取, - 那麼機器狀態將保持不變,並且退出(exit)原因將被標記為「錯誤」,其中包含最低的不可存取記憶體頁面地址(即試圖讀取但無法訪問的最小 RAM 索引)。 - 如果需要對 RAM 進行修改(寫入),但可變存取(mutable access)不可行, - 則機器狀態同樣保持不變,並且退出原因將標記為「錯誤」,其中包含最低的不可寫入的頁面地址。 ![v0.6.1 (4.26~27)](https://hackmd.io/_uploads/ryEqLB1Yyg.png) ![v0.6.0 (A.7~8)](https://hackmd.io/_uploads/SkCFcLYuJg.png) :::info $\mathbf{x}$ : the set of indices, modulo $2^{32}$ - 執行指令所需訪問的內存地址集合 $\varepsilon=*$ : 錯誤碼 + 最小無法訪問/修改的地址 $\mathbb{V}_{\mu}$: The set of validly **readable** indices for pvm ram $\mu$ $\mathbb{V}^{*}_{\mu}$: The set of validly **writable** indices for pvm ram $\mu$ - 沒有任何記憶體訪問錯誤 - 正常執行 $\blacktriangleright$ - 最低的地址 $\mathbf{x}<2^{16}$ - panic $⭍$ - otherwise : $page~fault$ 並且 return 最小無法訪問/修改的地址 ::: ### Signed/unsigned transitions for various octet widths ![v0.6.0 (A.9~14)](https://hackmd.io/_uploads/H1EXbvYu1x.png) (**指令表用到的 basic func**) :::info $\mathbb{B}_{8n}$ : The set of Boolean sequences/bitstrings. 長度為 $8n$ - $\mathcal{Z}_{n\in\mathbb{N}}$ : `uint -> int` - $\mathcal{Z}^{-1}_{n\in\mathbb{N}}$ : `int -> uint` - $\mathcal{B}_{n\in\mathbb{N}}$ : `uint -> vec<bool>` - $\mathcal{B}^{~-1}_{n\in\mathbb{N}}$ : `vec<bool> -> uint` - $\overset{\leftharpoonup}{\mathcal{B}}_{n\in\mathbb{N}}$ : `uint -> reverse(vec<bool>)` - $\overset{\leftharpoonup}{\mathcal{B}}^{~-1}_{n\in\mathbb{N}}$ : `reverse(vec<bool>) -> uint` ::: ![v0.5.4 (A.13)](https://hackmd.io/_uploads/rJr71FFP1l.png) (**指令表用到的 basic func**) - 即時參數以 little-endian 編碼,最高位(Most Significant Bit, MSB)表示符號位。 - 如果更高位元組(more significant octets)全為零或全為 255,則可省略這些位元組以壓縮表示。 - 被省略的位元組根據 MSB 決定補值: - 當值為正(MSB = 0)時,補值為零。 - 當值為負(MSB = 1)時,補值為 255。 - 此設計允許正數和負數的緊湊表示(compact representation)。 :::info $\mathcal{X}_n$ `uint -> int` 有符號數的擴展函數 (signed extension function) - 主要作用是解碼壓縮格式,還原完整的有符號數表示。 - 將 $n$ 個 octets 的 `uint` $x$ 擴展為對應的`int` - $2^{8n−1}$ : - $n$ 個 octets 的 MSB(符號位)對應的值。 - $2^{64} - 2^{8n}$ : - 補值用於處理緊湊編碼時被省略的位元組。 ::: ### Static jump ![v0.5.4 (A.14)](https://hackmd.io/_uploads/ryEftw9D1x.png) 如果 program counter 的目標位置不是一個基本塊的起始位置,則會觸發錯誤(panic)。這個檢查是為了確保跳轉的邏輯一致性並避免未定義行為。 :::info $b$ 跳轉目標的地址, $C\in\boolean$ : 是否執行 $\mathrm{branch}$ - $\neg C$: - if $C=False$,branch 操作不生效 - state, program counter 都不變 - $b \notin \varpi$: - if $C=True$,但跳轉目標 $b$ 不在基本塊的起始位置集合 $\varpi$ 中, - 觸發錯誤 $\text{☇}$ - program counter 不變 - otherwise - if $C=True$ `&&` $b\in\varpi$, 跳轉操作成功 - state 正常 - program counter 更新為 $b$ ::: ### Dynamic jump ![v0.5.4 (A.15)](https://hackmd.io/_uploads/H1P3kpcPkx.png) 動態跳轉的目標地址由跳轉表(jump-table, $j$)決定,且必須滿足以下條件: - 跳轉目標必須對應基本塊起始位置。 - 若目標地址無效,則會觸發錯誤(panic)。 - 跳轉地址的計算受 **跳轉對齊因子** $\mathsf{Z}_A=2$ 的限制。 :::info $a$ 跳轉的目標 address, jump alignment factor $\mathsf{Z}_A=2$ - $a=2^{32}-2^{16}$ 跳轉到指定位置 - state 停止 - program counter 不變 - 觸發錯誤 $\text{☇}$ 且 program counter 不變, if - $a=0$ - $a>|\mathbf{j}|\cdot\mathsf{Z}_A$ - $a\mod{\mathsf{Z}_A}\neq0$ - $\mathbf{j}_{(a/{\mathsf{Z}_A}-1)}\notin\varpi$ - 若以上都不符合,取跳轉表索引 $\mathbf{j}_{(a/{\mathsf{Z}_A}-1)}$ 的值作為目標地址 - 程序正常執行並更新計數器。 ::: ## A.5. Instruction Tables PVM 的指令集由 **有限集合的 opcodes** 定義,每個指令對應到: - 一個或多個 **位元組編碼 (opcode)** - 指令可能攜帶的 **操作數 (operands)** - 對狀態的作用(更新暫存器、記憶體、或程式計數器) - 是否為 **termination instruction**(決定 basic block 是否結束) ### 🔹 指令分類 1. **算術 / 邏輯指令 (Arithmetic & Logic)** - `add`, `sub`, `mul`, `div`, `rem` - `and`, `or`, `xor`, `not` - 立即數版本:`add_imm`, `sub_imm`, `and_imm`, ... 2. **比較 / 分支指令 (Comparison & Branch)** - `branch_eq`, `branch_ne` - `branch_lt_s`, `branch_lt_u` - `branch_ge_s`, `branch_ge_u` - 帶立即數版本:`branch_eq_imm`, `branch_ne_imm`, ... 3. **跳轉指令 (Jump)** - `jump` → 跳轉到固定位址 - `jump_ind` → 依 `j`(跳轉表)查目標位址 - `load_imm_jump`, `load_imm_jump_ind` ⚠️ **屬於 termination instructions** 4. **暫存器操作 (Register Ops)** - `load_imm` → 把立即數載入暫存器 - `mov` → 暫存器間拷貝 - `sethi` → 設定高位 bits 5. **記憶體操作 (Memory Ops)** - `load` / `store` - `load_imm` 搭配地址 - 存取規則由 (A.7–A.8) 的例外模型定義 6. **特殊控制 (Special / Termination)** - `trap` → panic 結束 - `fallthrough` → block 結束,執行下一條 - host 介面相關的呼叫 (`host_call`) --- ### 🔹 指令表 (簡化示例) | Opcode | 說明 | Operands | 狀態更新 | 是否終止 | |--------|------|----------|----------|----------| | `0x10` | add | r1, r2 → r3 | 暫存器 | 否 | | `0x20` | load_imm | imm → r | 暫存器 | 否 | | `0x30` | store | r → [addr] | 記憶體 | 否 | | `0x60` | jump | target | PC | ✅ 是 | | `0x61` | jump_ind | idx (查 j 表) | PC | ✅ 是 | | `0xFF` | trap | — | 結束 | ✅ 是 | ### 🔹 舉例:混合程式片段 ``` p = [0x20, 0x05, # load_imm a0, 5 0x20, 0x03, # load_imm a1, 3 0x10, # add a0, a1 → a2 0x60, 0x08, # jump 8 0xFF] # trap ``` - 指令解釋: 1. `load_imm a0, 5` 2. `load_imm a1, 3` 3. `add a0, a1 → a2` 4. `jump 8` → block 結束 5. `trap`(若被跳過不會執行) - 對應 block 劃分: - Block 1 = 指令 1~4 - Block 2 = 指令 5(trap) --- ### 📌 重點 1. Instruction Tables 定義了 **PVM 的完整 ISA (指令集架構)**。 2. 指令的長度可變(由 `k` 指定起點)。 3. **Termination instructions**:trap、fallthrough、各類 jump/branch。 4. 這些定義確保不同驗證者解析相同程式碼時,能得到完全一致的執行語義。 ## A.6. Host Call Definition 當遇到主機調用(Host Call)時的狀態機行為規則。Host Call 是一類特殊指令,當虛擬機運行到這些指令時,可能需要外部環境的協助來完成狀態轉換或資源分配(例如頁面錯誤或耗盡 Gas 的處理)。 **核心特性**: 1. 虛擬機會暫停並將 state 交給主機(Host)。 2. 虛擬機可根據 Host 回傳的結果重新運行。 3. 提供處理如資源不足、頁面錯誤等場景的彈性機制。 ![v0.5.4 (A.29)](https://hackmd.io/_uploads/H1kBwTcDJx.png) ![v0.6.4 (A.34)](https://hackmd.io/_uploads/ByviVK_nkg.png) :::info $\Psi_H$ : Host-call extension function $\mathbf{c}\in\mathbb{Y}$ : 指令碼 set $\imath\in\mathbb{N}_R$ : program counter $\varrho\in\mathbb{N}_G$ : gas $\omega\in[\mathbb{N}_R]_{13}$ : register $\mu\in\mathbb{M}$ : $\small\text{RAM}$ $f\in\Omega\langle X\rangle$ : Host function $\mathbf{x}\in X$ : additional data 執行完 $\Psi$ 將 state 更新成 $*^{\prime}$ 後, 若執行結果為 Host Call($\varepsilon' = \hbar\times h$),需要依賴 host-call function $f$ 更新 state,結果可能如下 : 1. 沒有進到 host-call function $f$ : - rise error $\varepsilon$ 2. 進到 host-call function $f$, 但是執行有錯誤 - $\mathbf{a}$ 指令地址讓 $\small\text{RAM}$ 錯誤導致 $\Finv$ 3. 進到 host-call function $f$ 且結果是正常($\blacktriangleright~or~\infty$) - 返回更新後的 state ($*^{\prime}$), 然後繼續執行 $\Psi_H$ -> state 更新成 $*^{\prime\prime}$ - 指令計數器 $\imath$ 更新為 Host Call 後的下一條指令: $\imath^{\prime\prime}=\imath^{\prime}+1+\text{skip}(\imath^{\prime})$ - 若出現錯誤: raise error $\varepsilon$ ::: ![v0.5.4 (A.30)](https://hackmd.io/_uploads/HJCYDp9vyx.png) :::info host call function $\Omega\langle X\rangle$ ::: 當 PVM 遇到 Host Call 指令時,如何處理指令計數器(program counter $\imath$)以及後續的狀態遷移邏輯。 1. 如果需要重新執行(例如遇到頁面錯誤或 Gas 耗盡),指令計數器 $\imath^{\prime}$ 應指向導致退出的指令,以便可以重試。 2. 如果 Host Call 成功完成,下一次執行應從 Host Call 指令之後開始,而不是重複執行相同的指令。 ## A.7. Standard Program Initialization ![v0.5.4 (A.31)](https://hackmd.io/_uploads/SkNgXRnPJe.png) PVM 的 single initializer function,包括內存分區、初始數據佈局以及如何解碼程序指令與內存狀態。 在 PVM 中,RAM被劃分為多個區域,分別用於: 1. Read-Only Data:存儲程序的靜態數據,例如常量。 2. Read-Write (Heap) Data:供程序動態分配數據。 3. Stack:存儲函數調用時的臨時數據。 4. Invocation-specific Data:一個額外的 Read-Only section,用於傳遞調用時的參數。 以上 sections 都會被 quantized 成 $major~zones$ 為了增強內存安全性: - 各內存區域之間留有未分配的間隔 - 緩衝區(Unallocated Zones),以避免意外的內存溢出。 - 每個區域向上填充 (by zeroes) 到最近的內存頁邊界。 初始化的主要目的是將 code $\mathbf{p}$ 以及附帶的參數 $a$ 映射到 PVM 的指令 $\mathbf{c}$, registers $\omega$、RAM $\mu$,以確保程序在一致的狀態下開始執行。 :::info standard initialization decoder function $Y$ - 如果 program code $\mathbf{p}$ 可以被唯一映射 ![v0.5.4 (A.32)](https://hackmd.io/_uploads/S1we8R2Pye.png) ![v0.6.5 (A.37)](https://hackmd.io/_uploads/BJ_UOVrJle.png) - $\mathbf{p}$ 的前三個 bytes 為 $\mathbf{o}$ 的長度 - 4~6 bytes 為 $\mathbf{w}$ 的長度 - 後面類推, 就可以得到 $(\mathbf{c}, \mathbf{o}, \mathbf{w}, z, s)$ - $z$ 是 zero-pages 的數量 - Otherwise -> $\varnothing$ ::: 成功映射後, 以下計算 input $\mathbf{c}$ 對應的 $(\omega, \mu)$ > 我就不仔細寫 A.36, A.37了 ![image](https://hackmd.io/_uploads/rkt31hJtkl.png) ![v0.5.4 (A.36)](https://hackmd.io/_uploads/Hkmu2RnP1l.png) ![v0.5.4 (A.37)](https://hackmd.io/_uploads/BkWh2Chwyg.png) :::info ![v0.5.4 (A.33~35)](https://hackmd.io/_uploads/HJ6a20hwke.png) 定義 - parameters $\mathsf{Z}_{Z}, \mathsf{Z}_{I}$ - $\mathsf{Z}_{Z}=2^{16}$ : The standard pvm program initialization zone size - $\mathsf{Z}_{I}=2^{24}$ : The standard pvm program initialization input data size - function $P, Z$ - $P$ 對齊頁面用 - $Z$ 對齊 zone 用? - 還有 $2^{32}$ (memory 的大小) 的判斷式, 確保不會超出記憶體的長度 ![image](https://hackmd.io/_uploads/HkL8ln1t1l.png) - 記憶體為 $2^{32}=4~\mathrm{GB}$ - $\mathsf{Z}_{P}=2^{12}$ 記憶體的頁面為 4 KB ![image](https://hackmd.io/_uploads/S1GPg31F1e.png) ::: ## A.8. Argument Invocation Definition ![v0.5.4 (A.38)](https://hackmd.io/_uploads/Bk-tWmAwJe.png) ![v0.6.3 (A.43)](https://hackmd.io/_uploads/ryj1H47o1e.png) ![v0.6.3 (A.43)](https://hackmd.io/_uploads/H1hzJldh1l.png) ![v0.6.3](https://hackmd.io/_uploads/BydIggd31l.png) **注意 R function 的第一個 $\varrho$** 應該是 $\Delta\varrho$ 定義 program-argument invocation function $\Psi_M$ :::info 嘗試將程式 $\mathbf{p}$ 使用 $R$ function 運作 Input : - $\mathbf{p}$ : program blob (service code hash) - $\imath$ : program counterr - $\varrho$ : gas - $\mathbf{a} \in \mathbb{Y}_{:Z_{I}}$ : 指令的地址? - $f \in \Omega\langle X\rangle$ : Host call function - $\mathbf{x} \in X$ : additional data Output : - 如果 $Y(\mathbf{p})=\varnothing$, 沒有成功映射到 PVM 的指令碼 - $\varrho$ : gas - $\text{☇}$ : panic - $\mathbf{x}$ : additional data - 如果 $Y(\mathbf{p})=(\mathbf{c}, \omega, \mu)$, 成功映射 - 運行 $R$ function (耗費 gas) ::: ![v0.5.4 (A.39)](https://hackmd.io/_uploads/rJpMzXCvJe.png) ![v0.6.3 (A.44)](https://hackmd.io/_uploads/BJOfB4Qiyg.png) :::info 根據 $\Psi_H$ output 的 $\varepsilon$ 來更新 states - $\infty$ : 執行完成 - $\halt$ : - 若 $\mu^{\prime}$ 可訪問 - 若 $\mu^{\prime}$ 不可訪問, 返回空地址 - $\panic$ : otherwise ::: 後面 PVM 有四個情境會會使用到這個 invocation,期望能夠**傳遞引數數據進入 PVM 並接收一些返回數據作為結果**。 - $\Psi_I$ (Is-Authorized) - $\Psi_R$ (Refine) - $\Psi_A$ (Accumulation) - $\Psi_T$ (On-Transfer)函數調用 ## A.9 Gas Model :::info Glossary - $\check{s}(\mathbf{c},\mathbf{k},\iota)$ returns a set of **source** registers read by a given instruction - $\check{r}(\mathbf{c},\mathbf{k},\iota)$ returns a set of **destination registers** which are written by a given instruction,regardless of whether those registers would actually have been modified by that instruction when executed at runtime. - $\check{c}$ is the number of **cycles** a given instruction needs to finish execution, - $\check{d}$ is the number of **decoding slots** necessary to decode it - $\check{x}$ is the **number of virtual CPU execution units** required to start its execution. ::: PVM 的 gas 成本模型是一個簡化版的「具管線化(pipelined)、亂序執行(out-of-order)」CPU 微架構模型,類似現代高階編譯器用來預測一段程式執行所需時間的模型。 對於程式中的每一個基本區塊(basic block),該模型會模擬其執行流程,並計算執行該區塊所需的虛擬 CPU 週期(virtual CPU cycles)數量。 對於從 instruction code index $\iota\in\varpi$ 開始的特定基本區塊,其 gas 成本 $\varrho_{\iota}$ 定義為該模型模擬出的虛擬 CPU 週期數量,直到該基本區塊內的所有 instruction 都被完成 ( retired ),且模擬達到收斂(converged)為止。 ![v0.7.1 (A.45)](https://hackmd.io/_uploads/SyYBPRIJWl.png) :::info $\Xi_{0}(\iota)$ : initial state of gas model, contains pc($\iota$) $\iota^{(0)}=\iota$ : zero-th pc scalar state : - $\dot{c}^{(0)}=0$ : cycle counter - $\dot{n}^{(0)}=0$ : step count - $\dot{d}^{(0)}=4$ : the number of available decoding slots in the pipeline front-end (capacity) - $\dot{e}^{(0)}=5$ : the size of the execution window - $\dot{x}^{(0)}$ : the number of virtual CPU execution units required to start its execution - $\text{A}$ : Arithmetric Logic Unit (ALU) - $\text{L}$ : Load - $\text{S}$ : Store - $\text{M}$ : Memory - $\text{D}$ : Dispatch vector state (index = instruction index in reorder buffer) : - $\vec{s}^{(0)}$ : a set of **instruction state** per instruction - 1: decoded - 2: pending - 3: executing - 4: finished - $\vec{c}^{(0)}$ : a set of **cycle count** per instruction - $\vec{p}^{(0)}$ : a set of **pending dependencies** per instruction - $\vec{r}^{(0)}$ : a set of **destination registers** per instruction - $\vec{x}^{(0)}$ : a set of **execution time** per instruction ::: ![v0.7.1 (A.46)](https://hackmd.io/_uploads/r1RvrxvyZg.png) Main STF function for state $\Xi$ from step $n$ to $n+1$ :::info $\Xi^{\prime}_{n+1}$ : decode new instruction (if room in decode buffer) - $n=0$ : directly decode if first step - $()\le \dot{d}^{(n)}$ : 還有空間可以 decode 下一個 instruction - $|\vec{s}^{(n)}|<32$ : ==32 bits?== $\Xi^{\prime\prime}_{n+1}$ : start execute pending instruction - $\mathfrak{S}\ne\varnothing$ 還有尚未執行的 instruction - 且尚有 execution unit available $\Xi^{\prime\prime\prime}_{n+1}$ : advance pipeline timing for all instructions in flight $\varnothing$ : terminate ::: 當 $\Xi_{m+1}=\varnothing$ 時, basic block 從 $\iota$ 開始執行 $m$ step 的 gas 消耗為: $$ \varrho_{\iota} = \max(\dot{c}^{(m)}-3, 1) $$ :::info - $\dot{c}^{(m)}$ : cycle counter - $-3$ : ==initailized penalty?== - minimal is $1$ ::: ![v0.7.1 (A.48)](https://hackmd.io/_uploads/HyQM7QwyWx.png) $\Xi^{\prime}_{n+1}$ 會 decode instruction :::info - $\Xi^{\text{mov}}_{n+1}$ : 如果 $\text{opcode}$ 解出來剛好是 $\text{move_reg}$ 的話就不需要 decode 到 buffer, 而是直接被 virtue CPU 的 frondend 處理 - $\Xi^{\text{decode}}_{n+1}$ : 其它情況, decode 到 reorder buffer 裡 ==Why $\text{move_reg}$ can execute fast?== ``` The `move_reg` instruction (register-to-register move) is special-cased: - It does not consume a decode slot. - It bypasses the reorder buffer entirely and is handled by the frontend. Reason: It has no data dependency or side-effect; thus, it can be executed immediately like a "zero-cost move" in modern superscalar CPUs (similar to register renaming). ``` ::: ![v0.7.1 (A.49)](https://hackmd.io/_uploads/ryXmiOPyZx.png) :::info - $\iota^{(n+1)}$ : 增加 pc - $\dot{d}^{(n+1)}$ : decode slot - 1 ($\text{mov}$ 不需 decode) - $\vec{r}^{(n+1)}_{j}$ : $j$-wise $\text{reg}$ operation - 若 source $\check{s}$ 與 dest. queue $\vec{r}$ 有交集 (我要讀取的 reg 已經有一部分在 queue 裡了) -> $\vec{r}$ 裡會去除 $\check{s}$ 且增加 $\check{r}$ -> 直接取聯集 - Otherwise, slash $\check{r}$ for $\mathrm{move\_reg}$ instruction ::: ![v0.7.1 (A.50)](https://hackmd.io/_uploads/HJ9bYfdk-l.png) :::info $\iota^{(n+1)}$ : $T$ (Termination instruction) $\dot{d}^{(n+1)}$ : 從 decoding slot 減掉此 instruction $\iota$ 所需的 slot $\dot{n}^{(n+1)}$ : step count $\vec{s}^{(n+1)}_{\dot{n}^{(n)}}$ : $1$ 代表 decode $\vec{c}^{(n+1)}_{\dot{n}^{(n)}}$ : cycle count $\vec{x}^{(n+1)}_{\dot{n}^{(n)}}$ : execution time $\vec{p}^{(n+1)}_{\dot{n}^{(n)}}$ : pending dependencies $\vec{r}^{(n+1)}_{j}$ : 把 $\dot{n}^{(0)}$ 的 $\text{reg}$ 讀進來, 然後把其他的 remove ::: ![v0.7.1 (A.51)](https://hackmd.io/_uploads/ByUdZV_1-l.png) 執行 pending 的 instruction :::info $\vec{s}^{(n+1)}_{\mathfrak{S}}=3$ : ==execution state?== $\dot{x}^{(n+1)}$ : 更新可執行的 execution unit $\dot{e}^{(n+1)}$ : 更新 execution window ::: ![v0.7.1 (A.52)](https://hackmd.io/_uploads/rJXJz4_kbl.png) :::info $\mathfrak{S}$ 決定 reorder buffer 中「最早可以開始執行」的 instruction(index $j$) - $\vec{s}^{(n)}_{j}=2$ : pending issued - $\vec{x}^{(0)}_{j}\le\dot{x}^{(n)}$ : 確保還有可執行的 execution unit - $\forall k\in\vec{p}^{(n)}_{j}\Rightarrow\vec{c}^{(n)}_{k}\le0$ : 確保沒有 dependencies 還在運行中 (in cycle) ::: ![v0.7.1 (A.53)](https://hackmd.io/_uploads/BkN31FdyWl.png) 用來更新 pipeline 上的各個 state 狀態 :::info $\vec{s}^{(n+1)}_{j}$ : instruction 在 pipeline 中的狀態 - 如果前 $j$ 個 instruction 都已經完成 $(4)$ -> 清掉 - 如果已經 decode 完成 $(1)$, 轉換成 pending state $(2)$ - 如果在執行階段 $(3)$ 且執行完畢 $\vec{c}^{(n)}_{j}=0$, 標記已完成 $(4)$ $\vec{c}^{(n+1)}_{j}$ : 剩餘執行 cycle - 如果這輪有執行 $\vec{s}_{j}=3$, cycle - 1 $\vec{r}^{(n+1)}_{j}$ - 如果這輪有執行 $\vec{s}_{j}=3$, 且是最後一個 cycle, 回收 $\text{reg}$ $\vec{x}^{(n+1)}_{j}$ - 回收已 commit 的 execution unit $\dot{c}^{(n+1)}$ : 增加一個 cycle $\dot{d}^{(n+1)}=4$ : 恢復到 initial state $\dot{e}^{(n+1)}=5$ : 恢復到 initial state ::: ## A.10 Gas Cost Tables $\mathfrak{P}(a, b|a<b)$ 判斷條件 charge less $a$ or more $b$ ![v0.7.1 (A.54)](https://hackmd.io/_uploads/Hy6TDp9JZe.png) :::info - if overlap betwenn $r$ and $s$, 不需要額外的 $\mathrm{reg}$ ::: ![v0.7.1 (A.55)](https://hackmd.io/_uploads/HJw4ua9yWx.png) :::info - For non-internediate shift or rotate 的 instruction ::: ![v0.7.1 (A.56)](https://hackmd.io/_uploads/By_nua5k-x.png) :::info memory access 取決於 L2/L3 HIT ::: ![v0.7.1 (A.57)](https://hackmd.io/_uploads/rkS-5a9y-l.png) :::info branch 是否造成 pipeline flush? ::: ## Appendix B. Virtual Machine Invocations ## B.1. Host-Call Result Constants Just Constants ## B.2. Is-Authorized Invocation ![v0.5.4 (B.1~2)](https://hackmd.io/_uploads/Sk5z520vkx.png) ![v0.6.4 (B.1)](https://hackmd.io/_uploads/HJRto-_2kg.png) ![v0.6.4 (B.2)](https://hackmd.io/_uploads/Hks3jZdnJx.png) - 第一個也是最簡單的呼叫器, 是完全 stateless 的 - 判定 WP $\mathbf{p}$ 跟核心 $c$ 是否授權被執行? :::info $\Psi_I$ 只提供一個 Host call function $\Omega_{G}$ 來決定剩下的 gas - input - $\mathbf{p}\in\mathbb{P}$ : WP 的 arguments - $c\in\mathbb{N}_{\mathsf{C}}$ : 應該被執行的核心 - output - 輸出 $\mathbf{r}$ - 正常執行結果 $\mathbb{Y}$ - error code $\mathbb{J}$ - 是 $\Psi_M$ 輸出的一部分 Is-Authorized host call dispatch function $F$ ![v0.5.4 (A.30)](https://hackmd.io/_uploads/HJCYDp9vyx.png) - 忽略了 context 因為要保持 **stateless**, 所以 context 永遠都是 $\varnothing$, ![image](https://hackmd.io/_uploads/ByVisGbd1e.png) ::: ## B.3. Refine Invocation Refine Invocation $\Psi_R$ 是 PVM 四個調用之一,設計用於支持服務賬戶的數據細化處理,主要特性包括: 1. 限制對 **Jam Chain** 的全局狀態訪問,**僅允許執行歷史查詢**。 2. 能夠創建嵌套的 PVM 實例,並將數據導出作為結果。 3. 歷史查詢的設計保證結果一致性,即使查詢鏈的某些狀態可能在審計時過時。 ![v0.5.4 (B.3)](https://hackmd.io/_uploads/r1XQumZdye.png) :::info inner PVM invocation host-call 會需要用到 $M$ 的 PVM type - $\mathbf{p}$ : 程式碼 - $\mathbf{u}$ : RAM - $i$ : program counter ::: Historical-lookup host-call function $\Omega_M$ - 設計成無論 JAM 的 state 為何都可以拿到同樣的數據 - lookup anchor 被設計最多是 $\mathsf{L}$ timeslots - L = 14, 400: The maximum age in timeslots of the lookup anchor. - 然後 $\mathsf{D}=4800$ - D = 28, 800: The period in timeslots after which an unreferenced preimage may be expunged ![v0.5.4 (B.4)](https://hackmd.io/_uploads/r1sGzNWOye.png) ![v0.6.0 (B.4)](https://hackmd.io/_uploads/SkUJ4vK_ke.png) ![v0.6.4 (B.5)](https://hackmd.io/_uploads/rkIch-uhJx.png) :::info 會需要用到 $\delta$ 來查詢 (來源必須要不早於 anchor block) input - $i\in\mathbb{N}$ : the index of the work item to be refined - $p\in\mathbb{P}$ : WPs - $\mathbf{o}\in\mathbb{Y}$ : author result - $\overline{\mathbf{i}}\in[\mathbb{G}]$ : import segment - G: The set of data segments, equivalent to octet sequences of length WG - $\varsigma\in\mathbb{N}$ : an export segment offset 匯出段的偏移量,用於管理數據的序列化。 output - 輸出 $\mathbf{r}$ 或是 error code + []? - 正常執行結果 $\mathbb{Y}$ - error code $\mathbb{J}$ ::: ![v0.6.0 (B.5)](https://hackmd.io/_uploads/H1liQDF_yg.png) ## B.4. Accumulate Invocation 會影響到很多鏈上的 state - **服務帳戶本身的數據**:與目標服務相關的狀態改變。 - **延遲轉移列表 (Deferred Transfer List)**:處理未來執行的資產轉移。 - **多個字典 (Dictionaries)**:用於管理以下具體狀態: - **預影像查詢 (Preimage Lookup) 狀態**的更改。 - **核心分配 (Core Assignments)**:計算資源分配的變更。 - **驗證者密鑰分配 (Validator Key Assignments)**:更新驗證者的公鑰映射。 - **新建帳戶 (Newly Created Accounts)**:記錄新帳戶的建立。 - **帳戶權限等級 (Account Privilege Levels)** 的更改:處理授權的動態變更。 ![v0.5.4 (B.6)](https://hackmd.io/_uploads/H1Iv0E-d1x.png) ![v0.6.5 (B.7)](https://hackmd.io/_uploads/rky-_Eryex.png) 定義 output context $\mathbf{X}$, 一般會有兩個維度 $\mathbf{X}\times\mathbf{X}=(x, y)$ - $x$ 是常規的, $y$ 是例外的 - 只有 checkpoint $\Omega_C$ 會更動到 $y$ :::info 上下文 XX X 定義了一組元素,用於描述累積調用的狀態和參數。其包含以下成員: - $s\in\mathbb{N}_S$:Service Id - $\mathbf{u}\in\mathbb{U}$:需要在 accumulation 期間更動的 state - $i\in\mathbb{N}_S$:import 的 service id? - $\mathbf{t}\in[\mathbb{T}]$:![image](https://hackmd.io/_uploads/ByqAlQGuke.png) - defer transfer 需要的參數 - $y\in?\mathbb{H}$:? ::: ![v0.5.4 (B.7)](https://hackmd.io/_uploads/ByndAVWd1e.png) 定義一個好用的 $\mathbf{x}_{\mathbf{s}}$ : accumulating 的 service account :::info - $\mathbf{x}_{\mathbf{u}}\in\mathbb{U}$ - ![image](https://hackmd.io/_uploads/ryXwvGGOJg.png) - $(\mathbf{x}_{\mathbf{u}})_{\mathbf{d}}[\mathbf{x}_{s}]$ : u 裡面的 service id ::: ![v0.5.4 (B.8)](https://hackmd.io/_uploads/HyrsCVZuyl.png) ![v0.6.4 (B.9)](https://hackmd.io/_uploads/rkrvUmdnyg.png) ![v0.6.5 (B.9)](https://hackmd.io/_uploads/rJWQ1kglel.png) Accumulation Invocation Function $\Psi_A$ : 用來調用 accumulation 函數的 caller :::info input - $\mathbf{u}\in\mathbb{U}$:需要在 accumulation 期間更動的 state - $t\in\mathbb{N}_{\tau}$:某個時間點 - $s\in\mathbb{N}_S$:service id - $g\in\mathbb{N}_G$:Gas - $\mathbf{o}\in[\mathbb{O}]$ ![image](https://hackmd.io/_uploads/SJH4B7Mdke.png) - operand tuples output - $\mathbf{u}\in\mathbb{U}$:更新後的授權上下文。 - $\mathbf{o}\in[\mathbb{T}]$:defer transfer 需要的參數 - H?H? H?:可選的雜湊值(若有需要)。 - $g\in\mathbb{N}_G$:剩餘的 Gas 數量。 ::: ![v0.5.4 (B.9)](https://hackmd.io/_uploads/S1fnR4-O1x.png) Initializer function $I$ 將某個服務帳戶 $s$ 與其索引 $s$ 映射,產生一個 Result Context $\mathbf{X}$,確保在任何退出場景下(正常或異常)都不會對狀態造成額外的更改(除了那些已經內含於 $s$ 的改變)。 需要注意的是,serviceId $s$ 使用隨機數 $\eta_0$ 和區塊的時間槽 $\mathbf{H}_t$ 來創建一個確定性標識符序列,該序列幾乎可以保證**唯一性**。 :::info input - $\mathbf{u}\in\mathbb{U}$:需要在 accumulation 期間更動的 state - $s\in\mathbb{N}_S$:service id output 前面定義的 output 格式 ![image](https://hackmd.io/_uploads/rJrn6MM_1l.png) - $s\in\mathbb{N}_S$:Service Id - $\mathbf{u}\in\mathbb{U}$:需要在 accumulation 期間更動的 state - $i\in\mathbb{N}_S$:import 的 service id? - 使用 check 函數確保在時間 $\mathbf{H}_t$ 時, $s$ 是尚未被 $\mathbf{u}_\mathbf{d}$ 使用的 ![v0.5.4 (B.13)](https://hackmd.io/_uploads/ByNzySbOkx.png) - $\mathbf{t}\in[\mathbb{T}]$: - defer transfer 需要的參數, 這邊帶空值 [] - $y\in?\mathbb{H}=\varnothing$ ::: ![v0.6.3 (B.10)](https://hackmd.io/_uploads/B1BzfIQokg.png) Mutator 變異器 $F$ 控制了在給定參數化的情況下,上下文將如何發生改變, :::info $\Omega\langle(\mathbf{X}, \mathbf{X})\rangle$ 直接帶入 $I$ 的結果 ![image](https://hackmd.io/_uploads/rymBsVMO1e.png) ![image](https://hackmd.io/_uploads/HJv2sEM_Jx.png) - ::: ![v0.5.4 (B.11)](https://hackmd.io/_uploads/ByaC0E-Okg.png) $G$ 目前不知道作用, 但是多一個 $\mathbb{A}$ 的 input - 感覺是跟沒有更動 state 有關的就用 $G$ 而不是 $\Psi$ - $n$ = R/W, lookup, info ![v0.5.4 (B.12)](https://hackmd.io/_uploads/rk6ykHWukx.png) ![v0.6.4 (B.13)](https://hackmd.io/_uploads/SySIw7dnJl.png) 崩解函數 collapse function $C$ 根據 ($if$ condition) 虛擬機終止時是常規還是異常來選擇上下文的其中一個維度。 :::info input - $g\in\mathbb{N}_G$:Gas - $\mathbf{o}\in\mathbb{Y}$ : author result or error - $(x,y)\in(\mathbf{X},\mathbf{X})$ : regular contex $x$, exceptional context $y$ output: 看起來有 error 就會用到 $y$, 正常狀況就是 $x$ - $*_{\mathbf{u}}\in\mathbb{U}$:需要在 accumulation 期間更動的 state - $*_{\mathbf{t}}\in[\mathbb{T}]$: - defer transfer 需要的參數 - $*_{y}\in\mathbb{H}?$ - 除非 $\mathbf{o}\in\mathbb{H}$ - $g\in\mathbb{N}_G$:Gas ::: 在極不可能的情況下,如果一個區塊在執行時發現某一服務索引被意外地附加到兩個不同的服務,則該區塊將被視為無效。由於沒有服務能預先預測標識符序列,因此無法故意使區塊作者處於不利地位。 ## B.5. On-Transfer Invocation ![image](https://hackmd.io/_uploads/r11WSBWukx.png) ![v0.6.0 (B.14~15)](https://hackmd.io/_uploads/HyxTpLq_kg.png) ![v0.6.4 (B.15~16)](https://hackmd.io/_uploads/BJRYu7d3Jl.png) On-Transfer service-account invocation function $\Psi_T$ (on-chain and stateful) - 跟 Accumulation invocation function $\Psi_A$ 很像 - 但是只容許更改 service account $\mathbf{s}\in\mathbb{A}$ 裡面儲存的值 - 在此調用過程中: - **不可發起進一步的轉帳(防止循環調用)** - **不可執行特權操作(確保調用安全)** - **不可創建新帳戶(防止資源濫用)** - **不可對目標帳戶執行其他操作(僅限餘額更新)** :::info input - $\mathbf{d}\in\mathbb{D}\langle\mathbb{N}_S\to\mathbb{A}\rangle$ : service id 找到 account - $t\in\mathbb{N}_T$ : timeslot,確保調用發生的時間可追溯。 - $s\in\mathbb{N}_S$ : service index,確保操作發生在有效帳戶上。 - $\mathbf{t}\in$ ⟦T⟧ : transfers,用於處理轉帳的具體數據。 - ![Eq. 12.14](https://hackmd.io/_uploads/rkPCocl4Jx.png) output - $\mathbf{s}\in\mathbb{A}$:更新後的帳戶狀態。 if service account 裡面的 actual code $\mathbf{s}_\mathbf{c}$ 或 transfer $\mathbf{t}$ 的內容為空 - service account $\mathbf{s}$ 不變 otherwise ![otherwise](https://hackmd.io/_uploads/SywHtD9O1l.png) - ![Psi_M](https://hackmd.io/_uploads/BykCDD5uyx.png) - call $R$ -> call $\Psi_H$ - ![v0.6.0 (B.16)](https://hackmd.io/_uploads/r1kq9wqOkx.png) - ![image](https://hackmd.io/_uploads/HJv2sEM_Jx.png) - otherwise : WHAT 是執行操作無效的訊號 where ![v0.6.0 (B.15)](https://hackmd.io/_uploads/Sy1Kmvq_kl.png) - $\mathbf{s} = \mathbf{d}[s]$ : source service account state , except $\mathbf{s}_b = \mathbf{d}[s]_b + \sum_{r\in\mathbf{t}}r_a$ (transfers 裡的 work reports) - $\sum_{r\in \mathbf{t}}{r_a}$ : 在 transfers 裡的 work report - $r_a$ : transfers 的 balance ::: ## B.6. General Functions 現在我們來定義 **主機函數(host functions)**,這些函數被 **PVM 調用(pvm invocations)** 使用。通常,它們將某些 PVM 狀態(包括調用上下文),可能再加上一些額外的參數,映射到新的 PVM 狀態。 ### General function form ![v0.6.0 General function](https://hackmd.io/_uploads/rkDHNO9u1g.png) 一些函數的輸出結果與輸入參數相同時,可以省略該結果的描述。 此外,某些函數可能依賴於額外的特定參數。($\mathbb{A},...$) 與 **累積函數(Accumulate functions,附錄 B.7)** 不同,這些函數 **不會改變 Accumulation context**,而僅僅影響 **服務帳戶 $\mathbf{s}$**。 Gas 計算函數 $\Omega_G$ 的參數列表以省略號結尾,表示它可以接受額外參數,並將這些參數透明地傳遞給結果輸出。這使得它可以在多個 PVM 調用中靈活使用。 ![v0.6.0 (B.17~18)](https://hackmd.io/_uploads/HyWZMvi_1l.png) 除了明確定義的 Gas 計數器外,**所有 PVM state 元素都==假定在主機調用中保持不變==,除非明確指定其變更**。 :::info General functions : $\Omega_G, \Omega_L, \Omega_R, \Omega_W, \Omega_I$ ::: ## B.7. Accumulate Functions ### Accumulate function form $$ ( \rho^{\prime}\in\mathbb{Z}_G, \omega^{\prime}\in[[\mathbb{N}_R]]_{13}, \mu^{\prime}, (\mathbf{x}^{\prime}, \mathbf{y}^{\prime}) ) =\Omega_{\square}( \rho\in\mathbb{Z}_G, \omega\in[[\mathbb{N}_R]]_{13}, \mu\in\mathbb{M}, (\mathbf{x}\in\mathbf{X}, \mathbf{y}\in\mathbf{X}),... ) $$ ![v0.6.0 (B.19)](https://hackmd.io/_uploads/rkkwUwi_yl.png) ![v0.6.0 (B.20)](https://hackmd.io/_uploads/SJAvLvj_yl.png) :::info Accumulate functions : - $\Omega_B$ bless (Empower-service host-call) - 設置特定的權限,包括 Bless、Assign 和 Designate 權限,這些權限存儲在 `ResultContext` 的 `PartialState.Privileges` 中。這些數據從記憶體讀取並解碼。 - $\Omega_A$ assign (Assign-core host-call) - 將授權隊列分配給特定的核心。它從記憶體中讀取數據,解碼為 `AuthQueue`,並存儲在 `ResultContext` 的 `PartialState.Authorizers` 中。 - $\Omega_D$ designate (Designate-validators host-call) - 設置驗證器數據。從記憶體中讀取數據,解碼為 `ValidatorsData`,並存儲在 `ResultContext` 的 `PartialState.ValidatorKeys` 中。 - $\Omega_C$ checkpoint (Checkpoint host-call) - 創建當前執行狀態的檢查點,將 `ResultContextX` 複製到 `ResultContextY`,並更新 gas 計數器。 - $\Omega_N$ new (New-service host-call) - create a new account - 創建一個新的服務帳戶。從記憶體中讀取代碼哈希,設置餘額和最小 gas 消耗等參數,然後生成一個新的服務 ID 並保存服務帳戶到 `PartialState.ServiceAccounts` 中。 - $\Omega_U$ upgrade (Upgrade-service host-call) - upgrade account balance - 升級現有的服務帳戶,更新代碼哈希和最小 gas 消耗參數。 - $\Omega_T$ transfer (Transfer host-call) - transfer $a$ balance to account_id $d$ - 轉移餘額從一個服務帳戶到另一個。檢查發送方餘額是否足夠,然後創建一個延遲傳輸交易,加入到 `DeferredTransfers` 列表中。 - $\Omega_J$ eject (Eject service host-call) - 釋放一個服務帳戶,將其擁有的資源返回給原始創建者。檢查特定條件(如服務帳戶的代碼哈希和時間槽信息)後,刪除該服務帳戶並將其餘額返回給原始服務帳戶。 - $\Omega_Q$ query (Query-preimage host-call) - 查詢指定哈希和長度的鏈接數據。它在服務帳戶的 `LookupDict` 中查找數據,並將結果返回到寄存器中。 - $\Omega_S$ solicit (Solicit-preimage host-call) - 記錄對特定資源的請求。它在 `LookupDict` 中查找數據,如果找到且符合特定條件,則添加當前時間槽到數據中。 - 請求 preimage 資料,preimage 內的 timeslot 長度紀錄 preimage 是否 available (詳細看 section 9.2.2)。 - $\Omega_F$ forget (Forget-preimage host-call) - 從 `LookupDict` 和 `PreimageLookup` 中刪除或更新特定資源的引用。該函數根據時間槽信息判斷資源是否需要被釋放。 - $\Omega_\Aries$ yield (Yield accumulation trie result host-call) - 將指定的哈希值作為異常保存到 `ResultContextX.Exception` 中。 ::: ## B.8. Refine Functions ![image](https://hackmd.io/_uploads/ByvBuDjOkx.png) 這些函數假設 refine context pair - $\mathbf{m}$ 是一個 **從自然數 NN N 到記憶體狀態 MM M 的映射**,代表細化操作所需的存儲結構。 - $\mathbf{e}$ 是一個 **數據段序列 ⟦G⟧**,通常代表與細化過程相關的中間數據。 這兩個結構在初始化時 **皆為空**。 ![v0.6.0 (B.21)](https://hackmd.io/_uploads/SymwOwjdyg.png) :::info General functions : $\Omega_H, \Omega_Y, \Omega_E, \Omega_M, \Omega_P, \Omega_O, \Omega_Z, \Omega_V, \Omega_K, \Omega_X$ :::