閱讀本頁,了解 Rand 上每一個證明背後的機器:它證明甚麼、程式的一次運行如何變成驗證節點可在數毫秒內檢查的東西,以及隱私從何而來。證明的成本與錢包如何生成證明,見證明。參數與哈希構造,見密碼學。程式所依據的指令集,見 RISC-V ISA。
每個證明都作出的同一個陳述
對於鏈上運行的每一個程式,zkVM 所證明的都是同一類陳述:
一個摘要為
hc的程式,自base_pc起動,在由H_IN承諾的私有輸入與由H_PUB承諾的公開輸入段上運行,於2ᵗ − 1個週期內停機,耗用至多GAS_LIMIT的燃料,並寫出了這八個輸出字。
一筆屏蔽轉帳,就是關於某個固定程式的這項陳述,該程式即隱藏資產捆綁客體程式,其 hc 在創世檔案中固定為 hc_bundle。自第 17 條鏈起,每筆轉帳會作出兩次證明:第二個較小的證明針對授權客體程式(創世檔案中的 hc_auth),用以證明發送者持有花費密鑰(證明)。機密調用則是關於已部署程式的同一項陳述。所有這些共用一台機器、一個驗證者和一組約束。因此,部署程式屬於數據操作(儲存其字與 hc),而非密碼學操作:不存在逐個程式的電路,也不需要可信設置。
證明系統是以 Plonky3 構建的 STARK。它只需要一個哈希函數:無需設置儀式,也不用橢圓曲線,因此沒有任何東西可供 Shor 算法破解。
流水線
secret input ──► run the program ──► execution trace ──► polynomials ──► commit ──► FRI ──► proof
(keys, (RV32IM emulator) (nine tables, (one per (8× code, (80 queries,
notes, paths) one row per step) column) Merkle) 20-bit grinding)
every validator: recompute the challenges from the Merkle roots, open 80 random positions,
check the rules there → ≈ 16 ms
上排只在發送者的機器上運行一次,捆綁包約需 100 秒,旁邊的授權證明約需 7 秒。下排在每個驗證節點上運行,僅憑公開欄位與證明本身。
1. 運行程式並記下一切
這台機器是 32 位元 RISC-V 處理器:RV32IM,去掉 FENCE、CSR 指令與 EBREAK(RISC-V ISA)。證明者先在模擬器上運行程式,並為每個時鐘週期記錄一行。這份記錄即執行軌跡。任何持有它的人都可以逐步重放整次運行;整套方案的其餘部分,目的是讓驗證者在不持有軌跡的情況下,也能確信軌跡無誤。
程式只通過八個系統調用與外界溝通,別無他途:
| 系統調用 | 編號 | 作用 |
|---|---|---|
HALT | 0 | 停止;八個輸出字即為最終結果 |
WRITE_OUTPUT | 1 | 設定八個公開輸出字之一 |
READ_INPUT | 2 | 讀取私有輸入的一個字 |
POSEIDON2 | 3 | 就地對記憶體中的緩衝區進行哈希 |
KECCAK | 4 | 一次 Keccak-f[1600] 置換 |
SHA256 | 5 | 一次 SHA-256 壓縮 |
READ_PUBLIC | 6 | 讀取公開輸入段的一個字 |
POSEIDON2_LEN | 7 | 將訊息長度綁定進哈希的 POSEIDON2(約束集 7) |
票據承諾、作廢符與 Merkle 路徑並非內建於機器之中。它們是普通的客體程式代碼:在記憶體中備妥帶域標籤的訊息,再調用 POSEIDON2。該系統調用不作填充,因此 [a] 與 [a, 0] 的哈希相同。已發佈的客體程式只對定長訊息調用它。若新程式要對長度可變的數據作哈希,則使用 POSEIDON2_LEN(RISC-V ISA)。
沒有中止
處理器沒有「拒絕完成」的指令,因此程式無法在檢查失敗時停下。捆綁客體程式改為記錄失敗:每次檢查失敗,就把 1 以 OR 運算寫入名為 bad 的暫存器,而 bad 是輸出摘要中最後被哈希的字:
D = H(64; anchor, nf₀..₃, cm₀..₃, fee, burn_a, burn_r, burn_asset, time, c, bad)
c 是授權承諾,授權證明同樣會公佈它(自第 17 條鏈起;第 14 至 16 條鏈的摘要沒有 c)。
鏈從不向發送者索取 D 的原像。它依據交易的公開欄位自行重建原像,自行設 bad = 0,然後哈希並比對。作弊者可以為一次檢查失敗的運行生成完全有效的證明,但該次運行輸出的是 H(…, 1),而鏈要找的是 H(…, 0)。要使兩者相符,需要一次哈希碰撞。
2. 把軌跡拆成九張表
以一張表描述整個處理器會過於寬闊,因此軌跡被拆成各司其職的專門表,並作為一個批次一併證明:
| 表 | 層級 t 下的行數 | 記錄內容 |
|---|---|---|
program | 自行聲明,≥ 2⁷ | 程式的指令字,在電路內解碼;限定 CPU 可取指的範圍 |
cpu | 2ᵗ | 每個週期一行,前面是對程式、私有輸入與公開段作哈希的行;載有 35 個公開值 |
memory | 自行聲明,≥ 2ᵗ⁺² | 暫存器與 RAM,按(空間、地址、時間)排序 |
alu | 2ᵗ⁺¹ | 每一次算術、邏輯、移位、比較、乘法與除法,以位元組肢體表示 |
range、nibble | 各 256 | 固定查找表:位元組、2 的冪、4 位元 AND/OR/XOR |
poseidon2 | 2ᵗ⁺² | 哈希置換,每輪一行 |
input | 自行聲明,≥ 2⁷ | 已承諾的私有輸入字 |
public | 自行聲明,≥ 2⁷ | 公開段的字(對捆綁包而言,即交易綁定) |
另有兩張表 keccak 與 sha256,只在程式調用過它們的證明中出現,出現時各至少 2⁷ 行;捆綁客體程式兩者皆不使用。每張自行聲明的表設有 2⁷ 行下限,是隨約束集 7 引入的(第 9 節)。
層級 t 為 10、12、14、16、18 或 20 之一,用以決定各表的大小。每個捆綁包都在層級 14 證明:CPU 表有 16,384 行,客體程式約用 12,400 行。授權客體程式在層級 10 運行 250 個週期。其餘為填充,因此證明只會顯示程式需要哪個層級,絕不會顯示它在該層級內走了多少步(層級)。
記憶體表使 RAM 變得可檢查。由於它先按地址、再按時間排序,「讀取返回該地址最後寫入的值」就成了相鄰兩行之間的一條規則。
3. 以匯流排連接各表
驗證者如何得知 CPU 所用的和,就是 ALU 所算出的和?答案是匯流排。當 CPU 需要一次加法時,它發送一條訊息 (op, a, b, c);ALU 對其計算的每一行接收一條訊息。這台機器有十七條匯流排:
| 匯流排 | 連接 |
|---|---|
PROGRAM、PROGRAM_WORD | cpu ← program:取指,以及程式摘要所吸收的字 |
MEMORY | cpu ↔ memory:每一次讀與寫 |
ALU | cpu → alu |
RANGE8、POW2、AND4、OR4、XOR4 | 針對固定表的位元組範圍檢查、移位與按位運算 |
POSEIDON2 | cpu → poseidon2:每次置換一條訊息 |
INPUT_DIGEST、INPUT_READ | cpu ← input:輸入摘要所吸收的字,以及程式的讀取 |
PUBLIC_DIGEST、PUBLIC_READ | cpu ← public:公開段的同一對匯流排 |
KECCAK、SHA256 | cpu → 可選的哈希表 |
BLIND | 每張表 → 固定循環中的下一張表:每張表在首行帶一個隨機值(約束集 7) |
這些匯流排以**對數導數論證(LogUp)**檢查。各表承諾之後,抽取一個隨機值 α,每張表對其發送或接收的訊息,保存 ±1/(α − m) 的運行和。當且僅當每條已發送的訊息都被接收,即兩邊的多重集相同時,這些和才會恰好抵消。哪怕偽造一條訊息,也會留下殘差,而隨機的 α 能以壓倒性的概率將其揭露。記憶體匯流排使用其近親,即多重集置換檢查。
每張表公佈其運行和的最終值,即其終值,驗證者檢查各終值之和是否為零。到約束集 6 為止,終值是其表內容的可檢查函數。自約束集 7 起,每張表多帶五個額外的列,並在首行發送一個隨機值,同時在 BLIND 匯流排上接收鄰表的隨機值。每個終值都會偏移挑戰域中的一個均勻元素,而總和仍為零。
摘要匯流排與讀取匯流排刻意分開成兩對。LogUp 按訊息而非按消費者平衡,因此若共用一條匯流排,證明者便可從摘要中丟掉一個字,而對該字的讀取仍然成功。
4. 把軌跡錨定到外部世界
軌跡輸出三十五個公開值,全部位於 CPU 表中:
| 數值 | 內容 |
|---|---|
| 1 | 入口 pc |
| 1 | 層級 |
| 8 | 輸出字詞(若為 bundle,即摘要 D) |
| 8 | hc,程式摘要,由程式表的字詞在追蹤內部計算得出 |
| 8 | H_IN,私有輸入摘要,對輸入計算,最前面加上全新的 128 位元鹽值 |
| 8 | H_PUB,公開區段摘要,不加鹽 |
| 1 | GAS_LIMIT,證明者宣告的 gas 上限(約束集 8) |
由於 hc 是在電路內計算,關於另一個程式的證明無法冒充為關於 bundle guest 的證明。驗證者從不接觸任何指令字詞,只會把 hc 與其預期的摘要比對。由於 H_IN 已加鹽,即使有人能夠猜出私有輸入,它也只是綁定該輸入而不會洩露其內容。由於 H_PUB 不加鹽,驗證者可以用自己已有的字詞重新計算。對 bundle 而言,這些字詞就是交易綁定:對已清空證明位元組的交易計算 blake3("rand-tx-bind-1" ‖ chain_id ‖ tx),因此若把證明複製到目的地、備註、封套或 chain id 不同的交易上,驗證不會通過。在 chain 20 上(創世 binding_domain: 1),則為 blake3("rand-tx-bind-2" ‖ genesis_hash ‖ chain_id ‖ tx),因此即使 chain id 相同,證明在另一條鏈上也無法通過驗證;這些字詞屬公開輸入,所以電路中沒有任何改動。授權證明是針對同樣的八個字詞作出的。chain 20 上的呼叫證明以同樣方式綁定:其區段是程式在部署時的公開字詞,後接八個呼叫綁定字詞(rand-call-bind-2,chain 20 之前為 rand-call-bind-1),因此若把呼叫證明複製到另一個費用 bundle 之下,驗證不會通過。
Gas 是機器衡量一次執行負載的單位,自約束集 8 起納入電路。CPU 表帶有一個 GAS 欄,初始值為 1,每一列加 1,Poseidon2 吸收列加 3,SHA256 列加 64,KECCAK 列加 192。在 HALT 列,電路透過差值的四個經範圍檢查的分段來證明 GAS ≤ GAS_LIMIT,這些分段從不公開:上限是公開的,而一次執行實際耗用的 gas 則不是。驗證者在建立金鑰之前,會拒絕高於該證明自身標頭可能需要的上限的任何宣告上限:
gas_max(t, k, s) = (2ᵗ − 1) + 2ᵗ⁻² + 191·(2ᵏ / 32) + 63·(2ˢ / 64)
其中 k 和 s 分別是 keccak 表與 sha256 表所宣告的對數高度,表不存在時對應項為零。不含雜湊的 tier-10 標頭,其上限為 1,279,tier-14 標頭則為 20,479。鏈按呼叫所宣告的上限計價(費用)。
5. 將規則轉為多項式恆等式
每一欄都是 Goldilocks 域中的一列數字,即模 p = 2⁶⁴ − 2³² + 1 的整數。n 個數字的列表等同於一個次數低於 n 的多項式:當這些數字排列在域的子群 {1, ω, ω², …} 上時,該多項式恰好通過它們。Goldilocks 擁有大小為 2 的每個冪次(最高 2³²)的子群,因此這種轉換可用快速傅立葉變換完成。
機器的一條規則隨後成為各欄多項式之間的一條方程式。「程式計數器每次前進四,除非該指令跳轉」可以示意性地寫成:
is_jump(x)·(pc(ωx) − target(x)) + (1 − is_jump(x))·(pc(ωx) − pc(x) − 4) = 0
對子群中的每個 x 成立,其中 pc(ωx) 指「下一列」。一張表的全部此類規則,就是它的 AIR(代數中間表示)。CPU 和 ALU 的規則達到 8 次;其餘維持在 2 至 4 次。
在子群每一點上皆為零的多項式,可被 Z(x) = xⁿ − 1 整除。證明者以隨機權重把所有規則合併為單一多項式 C(x),再除以 Z(x)。若每條規則在每一列都成立,商就是一個真正的低次多項式;若有一條規則在某一列不成立,則商不是。「一次 16,000 個週期的執行,每一步是否都正確?」就變成了「這個函數是否接近某個低次多項式?」,而這個問題只需查看少量隨機選取的值就能回答。
6. 在問題揭曉之前先作承諾
證明者必須在看到驗證者的隨機提問之前固定其多項式,否則它可以針對提問度身訂造答案。它在比表大八倍的域上計算每一欄的值(膨脹係數 8,碼率 1/8),並公佈這些值的默克爾根。這種冗餘是一種里德–所羅門碼:低次多項式由其任何八分之一的值所確定,因此哪怕只改動追蹤中的一列,作弊者也必須改動擴展碼字的大部分才能保持一致,而隨機抽查會命中被改動之處。
默克爾樹使用 Goldilocks 上的 Poseidon2:寬度 8,S-box 為 x⁷,4 個完整輪、22 個部分輪,再接 4 個完整輪,摘要為四個域元素(約 256 位元)。每個葉節點都以四個全新隨機元素進行雜湊,因此根不會透露其下任何數值。同一個置換在機器內部同時用作票據承諾與無效化符雜湊。它的 86 個輪常數是節點原始碼中的字面表(poseidon2_constants.rs),因此任何函式庫升級都無法更改它們。
7. 以 FRI 抽查
驗證者此時持有默克爾根。它首先把商與追蹤連繫起來:在表的域之外隨機選取一點 z,索取每一欄以及商在該點的值,並直接檢查 C(z) = Q(z)·Z(z)。餘下需要確認的是,所承諾的函數確實是低次多項式,而這正是 FRI 所測試的。
FRI 以摺疊的方式運作。把多項式拆成偶數部分與奇數部分,f(x) = fₑ(x²) + x·fₒ(x²)。取驗證者給出的隨機 β,f′(y) = fₑ(y) + β·fₒ(y) 在大小減半的域上,次數也減半。證明者對 f′ 作出承諾,取得新的 β,再次摺疊(Rand 每輪最多摺疊八倍),直至剩下一個常數,並將其明文發送。隨後,一次查詢在第一層隨機選取一個位置,並沿著各層向下追蹤,在每一層檢查上一層所打開的值確實摺疊為下一層的值。一個遠離低次的函數,無法讓所有這些承諾保持一致。
Rand 進行 80 次查詢。在已證明的接近度間隙定理之下,每次查詢最多讓作弊者有 9/16 的機會得逞,因此 80 次查詢給出 (9/16)⁸⁰ ≈ 2⁻⁶⁶·⁴。在 ethSTARK 猜想(已部署的 STARK 所依賴的猜想)之下,每次查詢只剩 1/8,遠遠更小。
8. 去除對話
上文都以對話方式描述,但交易必須是任何驗證節點都能獨立檢查的單一訊息。Fiat–Shamir 變換去除了驗證者:證明者以 Poseidon2 雙工海綿,把每個「隨機」挑戰計算為迄今所有承諾的雜湊。更改任何承諾,其後的每個挑戰都會改變,因此證明者無法挑選配合挑戰的承諾。
另有兩項措施填補由此留下的缺口:
- 擴展域。 擁有
2⁶⁴個元素的域太小,不足以從中抽取挑戰:作弊者只需讓挑戰落在多項式少數幾個不良值之一,無論進行多少次查詢,成功機率都約為N/2⁶⁴。因此追蹤仍留在 Goldilocks 中,但每個挑戰都取自其二次擴展域,約有2¹²⁸個元素。 - 研磨(grinding)。 在推導查詢位置之前,證明者必須找到一個隨機數,使其與轉錄的雜湊以 20 個零位元開頭,約需一百萬次雜湊。誠實的證明者只需付出一次。作弊者若不斷重擲查詢位置,直至避開其偽造之處,則每次嘗試都要付出這個代價。這是 Rand 中唯一的工作量證明,與共識毫無關係。
合計:查詢帶來 66.4 位元,加上研磨的 20 位元,得出 86 位元已證明;在猜想之下約為 100 位元,其中擴展域的項為上限。
9. 零知識從何而來
STARK 本身並非零知識:在查詢位置打開的值是追蹤多項式的值,若不加處理便會洩露資訊。Rand 的配置增設四層保護:
- 每個已承諾的多項式都經隨機化(Plonky3 的隱藏式 FRI 混入四個隨機碼字),因此這 80 個打開的值,可由從未見過追蹤的人在分佈上重現;
- 每個默克爾葉節點都已加鹽,因此根與認證路徑不透露任何資訊;
- 證明者的隨機性在每次證明時都從作業系統重新取得,因此同一筆付款的兩個證明看起來互不相關;
- 每張已宣告的表都至少有
2⁷列,每個 LogUp 終端值都已遮蔽(約束集 7)。一個證明在 80 個查詢點和兩個域外點打開每張表,而隱藏式承諾所混入的隨機列數與表的列數相同,因此一張表只有在至少有 82 列時才算被隱藏。128 是滿足此條件的最小二的冪。
Layer 4 是一項更正。2026-09-27 的審查從生產環境的證明中,還原了輸入字數不超過 31 的調用的私有輸入,從程序的簡短程序表讀出其 fetch 次數,並憑未遮蔽的終端值分辨出一個 bundle 的真實輸入槽與虛設槽。這些證明是在 chain 15 及更早的鏈上生成的。Chain 16(constraint set 7,採用無分支的 bundle guest)已修補這三項問題,chain 18 至 20 沿用有關修正。
保證為隨機預言機模型下的統計零知識。證明不隱藏的內容是明確的:它的層級(tier)、其聲明的表高度,以及它的公開值,包括 gas 上限。聲明的 keccak 或 sha256 高度為 0,即表示該次運行從未使用該雜湊函數,因此調用證明的標頭會顯示該調用有否進行雜湊運算;若高於下限,還能顯示其次數(誤差在兩倍以內)。就 bundle 而言,公開值與欄位包括錨點(anchor)、四個 nullifier、四個承諾、費用、銷毀額、銷毀資產、時間、授權承諾,以及交易綁定。轉賬所移動的資產屬於私有輸入,因此 zUSD 付款與 RAND 付款在鏈上看起來並無分別。hc 是確定性的,所以任何人都能得知一個證明涉及 bundle guest(每個轉移價值的證明皆然),或就調用而言,得知它運行的是哪個已部署程序。
10. 驗證者檢查甚麼
Machine::verify_public(hc, words, proof) 按成本由低至高執行以下檢查:
- 證明所聲明的層級與表高度須在界限之內(
check_declared_heights),然後才按這些數值分配任何資源,因此格式有誤的標頭只需數次比較,而不必花上數秒構建密鑰;bundle 必須聲明 tier 14,且不含任何雜湊表,授權證明則須為 tier 10; - 公開值共有 35 個,每個均為規範的域元素,且
pv[HC] = hc,pv[TIER]等於證明的層級,每個輸出字均小於2³²,而GAS_LIMIT不超過標頭的gas_max; - 批次 STARK 以按形狀
(tier, program, input, keccak, sha256, public heights)緩存的驗證者密鑰驗證,而非按程序緩存; H_PUB等於words的摘要,對 bundle 而言,這就是節點自行計算的交易綁定。
在上述任何步驟之前,節點會先篩查每個提交或經 gossip 傳播的證明,檢查其編碼是否規範:其 FRI 折疊排程必須與誠實的證明者就標頭所聲明的次數而生成的排程一致。自 fullnode v0.6.8 起(審計 v6,ZKV-4),篩查會先拒絕任何聲明次數超過 25 位元的證明(MAX_HONEST_DEGREE_BITS,即記憶體表的 2²⁴ 上限加上零知識倍增,亦即已接受證明所能攜帶的最大值)。在設定此界限之前,聲明極大次數的標頭會令節點在其事件迴圈中重新推導龐大的折疊排程,並可由發送者任意延長佔用時間;驗證者本來也會拒絕該證明,因此這項界限改變的只是拒絕的成本。篩查內部出現 panic 時,現時只會拒絕該筆交易,而不會令節點停止運行。
隨後,賬本以 bad = 0 重新計算 bundle 摘要 D,並與證明的八個輸出字比對。在 chain 20 上,賬本亦要求 bundle 證明的 GAS_LIMIT 必須恰好等於創世的 bundle_gas_limit,即 20,479,授權證明的則為 1,279,因此每筆轉賬所聲明的數值都相同。密鑰已預熱時,整個檢查約需 16 毫秒。
參數及其選定原因
下列每個數字都屬共識:驗證者會檢查它,以任何其他數值生成的證明均無法通過驗證,而更改其中任何一個即屬新的約束集及硬分叉(Constraint sets)。FRI 配置由創世文件(chain 20 上為 fri_profile: production)決定,絕不由證明決定。
| 參數 | 數值 | 取值理由 |
|---|---|---|
| 基域 | Goldilocks,p = 2⁶⁴ − 2³² + 1 | 一個 64 位素數,其運算只需數條機器指令;它的乘法子群涵蓋直至 2³² 的每一個二的冪大小,因此把一列數據轉成多項式,就是一次快速傅立葉變換。執行軌跡(trace)置於此域,因為它是成本最低的域。 |
| 挑戰域 | 二次擴域 F_p²(≈ 2¹²⁸) | 若挑戰直接取自 Goldilocks,會留下約 N/2⁶⁴ 的可靠性下限,無論查詢次數多少都無法壓低。擴域把這一項壓至不超過 2⁻¹⁰⁰,適用於最多 2²⁵ 行的軌跡。只有挑戰需要承擔較大的域,軌跡則不用。若日後需要 128 位,三次擴域(≈ 2¹⁹²)是已定的備用餘量。 |
| 擴展倍數(blow-up) | 8(碼率 ρ = 1/8,log_blowup = 3) | 有兩個理由。CPU 與 ALU 表的規則次數為 8,啟用零知識旗標後,商多項式的次數為 9,這是擴展倍數 8 所能承載的上限;次數更高的規則便會迫使擴展倍數升至 16。此外,每次查詢捕捉作弊者的能力取決於碼率:證明所得為每次查詢 (1 + ρ)/2 = 9/16,猜想為 ρ = 1/8。擴展倍數越大,所需查詢越少,但代價是證明者須計算並存放於記憶體的低次擴展按比例增大。 |
| FRI 查詢次數 | 80 | 這個數字使已證明的可靠性維持在約 86 位。它曾被削減至 27(約束集 3 與 4),達到猜想的 100 位目標(3·27 + 20 = 101),但已證明的位數只剩約 42;2026-09-12 的零知識審計(發現項 ZM1)將其恢復為 80。查詢佔證明大小的大部分,因此一個 bundle 證明約為 1.50 MB。若要證明 100 位,則需 97 次查詢。 |
| 磨礪(grinding) | 20 位,置於查詢階段 | 在抽取查詢位置之前,證明者必須找到一個隨機數(nonce),使其哈希以 20 個零位開頭:約需一百萬次哈希,誠實的證明者只需付出一次,作弊者每次重新抽取位置都要再付一次。它以低成本增加 20 位;更多的磨礪(34 位亦可得出可證明的 100 位)會令每個誠實證明都更慢。承諾階段不設磨礪。 |
| 摺疊元數(folding arity) | 每輪最多 8(max_log_arity = 3) | 以八而非二進行摺疊,FRI 層數減至三分之一,因此每次查詢需開啟的 Merkle 路徑較少,證明亦較小,代價是每層的工作量略增。 |
| 最終多項式 | 常數(log_final_poly_len = 0) | FRI 一直摺疊到底,並以明文傳送一個值,這是驗證者最簡單的終端檢查。 |
| 哈希(電路內、Merkle、transcript) | Poseidon2,寬度 8,S-box x⁷,4 + 22 + 4 輪,碼率 4,容量 4 | 這種哈希專為以域運算實現而設計,成本低廉,因此在證明內進行哈希(承諾、nullifier、Merkle 路徑、程式摘要)只消耗行數,而非數以千計的位元運算。S-box 的指數不得與 p − 1 = 2³²·3·5·17·257·65537 有公因數,7 是可行的最小值。寬度 8、碼率 4 得出四個元素的摘要,約 256 位。86 個輪常數是節點中的一份字面表(poseidon2_constants.rs)。 |
| 摘要寬度 | 4 個域元素(≈ 256 位) | 足以應付原像類用途(nullifier、承諾:對抗 Grover 為 128 位)。但對 transcript 中 128 位的量子碰撞抵抗而言則不足(2²⁵⁶/³ ≈ 2⁸⁵);白皮書的設計要求使用 384 位的 transcript 哈希,而運行中的鏈尚未採用。 |
| 隱藏性 | FRI 中 4 個隨機碼字;每個 Merkle 葉 4 個鹽元素;每個證明使用新的作業系統隨機數 | 這是令 80 個開啟位置與 Merkle 路徑僅憑公開值即可模擬的最低要求,因此證明具統計零知識性。同一筆付款證明兩次,所得結果看來互不相關。 |
| 表格下限 | 每個已宣告的表格至少 2⁷ 行(約束集 7) | 一個證明會在每個表格上開啟 80 個查詢點及 2 個域外點,合共 82 個;而 h 行的表格由 h 個隨機行遮蔽。64 行太少,128 是下一個二的冪。查詢次數若超過 126,則需要 2⁸。 |
| 終端遮蔽(terminal blind) | 每個表格一個 F_p² 的值,置於 BLIND 總線(約束集 7) | LogUp 的終端值由原始軌跡計算而得,若無遮蔽,它便是其表格的無鹽承諾。遮蔽值取擴域元素,是因為只用一個基域坐標,會把偏移限制在一個稀疏集合內,候選見證便可對照該集合進行測試。 |
| gas 權重 | 每行 1;每個 Poseidon2 吸收行 3;每個 SHA256 行 64;每個 KECCAK 行 192(約束集 8) | 每個權重計算該行牽引到其他表格的行數:一個吸收行牽引一次 32 行的置換,一個 SHA256 行牽引 466 列的 64 行,一個 KECCAK 行牽引 2,612 列的 32 行及 100 個記憶體行。 |
| 最大約束次數 | 8 | 由上述擴展倍數決定;每個表格的次數均由測試固定,以免新規則在無聲無息間超出上限。 |
| 層級(tiers) | 10、12、14、16、18、20 | 表格按六個固定級別定大小,每級為上一級的四倍,而證明只揭示其層級:即運行長度的一個粗略、經填充的區間,而非其週期數。每個 bundle 均為層級 14,每個 auth 證明均為層級 10;自 fullnode v0.5.6 起,調用上限為層級 14。 |
| 最大軌跡 | 2²⁴ 行(啟用零知識位元時為 2²⁵) | 擴域一項所證明適用的大小;較長的軌跡會被拒絕,而不會在不成立的可靠性聲明下進行證明。 |
| 公開值 | 35 個域元素 | 入口 pc、層級、八個輸出字、三個八字摘要 hc、H_IN、H_PUB,以及所宣告的 gas 上限:這是能指明程式、私有輸入、公開輸入及工作量上限的最小陳述。在約束集 7 之前為 34 個。 |
| 證明大小上限 | 預設 2 MiB;chain 20 為 4 MiB(max_proof_bytes) | 2 MiB 高於已部署 guest 在不含哈希表時所生成的每一個證明,而低於含 keccak 的證明(≈ 3.3 MB);chain 20 的創世區塊把它設為 4,194,304 以容納兩者,chain 17 至 19 亦然。Chain 13 至 16 設為 8 MiB。此上限在證明解碼之前檢查。 |
這些數字合起來意味著什麼:已證明的可靠性為 86 位,在 ethSTARK 猜想下約為 100 位(其中的限制來自擴域而非查詢次數),而對抗量子對手則約為 85 位,由 256 位的 transcript 哈希決定。白皮書刻意將兩個可靠性數字並列:每一個已部署的 FRI-STARK 都靠該猜想支撐最後的若干位,而為部署定規模的讀者應知道哪一項假設承載哪些位。
證明之證明
一個 bundle 證明約 1.50 MB,份量不輕,1.37 MB 的 auth 證明與之並行,而每個節點都要儲存全部。已建成的補救辦法是遞歸:一個證明,其陳述為「我已驗證這些證明」。fullnode 為此配備第二部較小的機器,即遞歸 VM,其原生字為一個 Goldilocks 元素,指令即驗證者自身的運算(域運算、Poseidon2、Merkle 與 FRI 步驟)。其聚合程式會重演 N 個 bundle 證明的完整驗證,並公佈一個涵蓋其公開值的摘要,綁定至該鏈、aggregator 與一個 nonce。在遞歸 VM 內驗證一個 bundle 證明,在約束集 6 下耗用 1,968,619 行,在約束集 7 下為 2,044,506 行;在約束集 8 下尚未重新量度。它已實現,並在迄今每條鏈上均由創世區塊關閉,chain 20 亦然。聚合程式只驗證 bundle 證明,因此在採用拆分授權的鏈上,auth 證明仍會保留。其生產環境的證明所需記憶體亦超出原先的規劃:在約束集 7 下量度為 133 GB 以上(Upcoming: block aggregation)。
本方案不作的聲明
- 量子安全餘裕約為 85 位元,而非 128 位元。 對
n位元雜湊進行量子碰撞搜尋,成本約為2ⁿ/³。白皮書的設計要求使用 384 位元的轉錄雜湊;現行區塊鏈使用的是 256 位元的 Poseidon2 摘要。要加寬它,須採用新的約束集,並進行硬分叉。 - 100 位元只是猜想,86 位元才是已證明的數字。 若要把已證明的數字提高至 100,需要 97 次查詢,即每個證明的查詢數據增加約五分之一。
- Poseidon2 常數是以種子抽取得出,並非已公開發表的實例化。目前未知有針對這一點的攻擊;若採用標準常數集,鏈上每一個雜湊都會改變。
- 部分陳述是在證明之外核對,而非在證明之內。
TIER公開值由驗證者與證明的形態比對,但電路並未對其施加約束(審計 ZL1);而 bundle guest 的污點紀律,靠的是結構性論證、削弱版 guest 測試框架及變異模糊測試來支持,並非經機器檢驗。 - 證明的標頭並未隱藏。 層級以及兩個雜湊表的高度均為明文,因此一個調用證明會顯示該調用使用的是
KECCAK還是SHA256。目前存在一個把兩個表都填充進去的證明者選項,但沒有任何錢包啟用它:這會使層級 10 的證明由約 1.3 MB 增至約 3.6 MB。 POSEIDON2系統調用對於可變長度的輸入並非單射。 由於每一個承諾、nullifier、根及程式摘要都依賴它,故保持現狀。目前尚無已發布的 guest 使用POSEIDON2_LEN。- 證明速度緩慢,由發送者自行完成,或由發送者配對的證明者完成:在筆記型電腦的 CPU 上,一個 bundle 約需 100 秒及 5.7 GB。GPU 內核於 2026-09-28 首次在 GPU(NVIDIA H100)上運行,結果與 CPU 參考實現一致;錢包目前仍使用 CPU 進行證明。
延伸閱讀
- 白皮書的章節「The Zero-Knowledge Scheme: How a Rand Proof Works」(
randprotocol.tex) 以同樣的七個要素,連同可靠性定理逐一說明。 - 實現論文 (
randprotocol_implementation.tex) 規範了指令集與系統調用、bundle guest、海綿結構與摘要、每個表與總線、機器配置,以及遞歸虛擬機。 - 程式碼:fullnode 中的
crates/randprotocol-zkvm/src/—emulator.rs、tables/、machine.rs、gas.rs、guests.rs、hash.rs、poseidon2_constants.rs。