自去年夏天的主題論文和 Lasso 部署開始,到上個月的完全開源的 Jolt 實現的發布,我們一直都在致力于 Lasso+Jolt(我們的全新簡便的高性能 lookup argument 和 zkVM)技術的研究并取得穩步進展。
與現有技術相比,這一實現顯示了 Jolt 的光明前景,并對 SNARK 設計中的許多傳統智慧發起挑戰。自發布以來,我們陸續進行更新,增加了對 Rust 標準庫的支持,整合了來自 10 多名貢獻者的改進,合并了近 50 個 pull 請求,并且改進了代碼庫的模塊化性能和可擴展性。
在我們繼續增強 Jolt 的同時,我想回應外界的質疑和困惑,澄清誤解,分享我對一些關鍵問題的看法。我在本文要探討的四部分內容是:(1)sum-check 協議與 Binius 承諾方案之間的關系,(2)sum-check 和 lookups 在 Jolt 中的作用,(3)橢圓曲線與哈希,(4)與 zkVM 相關的預編譯。
1、Sum-check 協議與 Binius 承諾方案
Commitment schemes 通常被視為 SNARK 的關鍵組成部分。但還需注意另一個組件的作用也很重要,那就是多項式 IOP。例如,多線性多項式的 Binius 承諾方案是一個重大進步,但它必須與多項式交互式 oracle 證明(polynomial interactive oracle proof,PIOP)配對,才能證明所提交的數據實際上驗證了證明者的聲明。
Binius 的承諾與使用 sum-check 協議的 PIOP 高度兼容。原因很清楚(sum-check 依賴于多線性多項式,而不是單變量多項式;FRI-Binius 甚至在內部使用 sum-check),也很微妙(sum-check PIOP 天然地跨任何特征字段運行,這對于充分利用 Binius 的新性能至關重要)。Binius 的承諾與目前最常見的 PIOP 不兼容,很遺憾,這些 PIOP 不使用 sum-check。
設計一個快速的 PIOP 需要更多的洞察力,而不僅僅是「應用 sum-check」這一句話而已。Binius 使用 sum-check 協議來實現高效的多項式 IOP。Binius 論文的第 4 和第 5 部分致力于設計新的高效的基于 sum-check 的 PIOP,以與承諾方案相結合。
Binius 承諾和 Jolt 的搭配就像花生醬和果醬一樣,因為 Jolt 是目前唯一一個完全基于 sum-check 協議的 zkVM。如今,Jolt 使用基于橢圓曲線加密的承諾方案,但將 Binius 承諾納入 Jolt 是我們工作的重中之重。
2、Sum-check、lookups、性能和簡潔性
是什么讓 Jolt 與眾不同?是因為 Jolt 是第一個(也是目前唯一一個)專門使用基于 sum-check 的多項式 IOP 的 zkVM,還是因為 Jolt 實現了 lookup 奇點(幾乎所有的事情都是通過 lookups 而非約束(constraint)系統或電路來完成的)?答案是,兩者兼有。與之前的 zkVM 相比,Jolt 的大部分的簡潔性優勢都來自于 lookups,而它的性能優勢來自于 lookups 和 sum-check 的使用。
單純的 lookup 方法對于某些指令(沒有非常小的電路的指令)更好些,但是對于具有非常小的電路的其他指令可能要更差。但總的來說,單純的 lookup 方法對性能來說只有好處沒有壞處,至少在處理 256 位字段時如此。如今,Jolt prover 投入 20% 的時間在「指令執行」lookup 上,40% 的時間用于驗證約束信息。添加更多約束來減少 lookups 是沒有任何幫助的。
大概說來,Jolt 使用 lookups 來實現 CPU 獲取 – 解碼 – 執行循環的「獲取」和「執行」部分。這些 lookups 速度足夠快,以至于 prover 的大部分時間都用于證明它運行了「解碼」,這是通過傳統的約束來處理的。
單純的 lookup 方法還會促進更簡潔、更可審計的實現。這些好處很難被量化,需要時間才能被看見和認可。但在代碼行數(Jolt 代碼庫約為 2.5 萬行代碼,比之前的 RISC-V zkVM 少 2 到 4 倍)和開發時間等方面,Jolt 表現出色。這樣的改進要比性能上的改進難得多:雖然我預計 zkVM prover 在未來幾個月的速度將比 2023 年 8 月差不多快百倍,但很難想象 zkVM 的代碼行數什么時候能減少 10 倍。
3、橢圓曲線
公共話語低估了擁有針對橢圓曲線的快速 zkVM 的好處,部分原因是大家普遍對基于哈希的承諾方案(如 Binius)熱情滿滿。
在證明關于橢圓曲線加密的聲明時,基于曲線的 zkVM 可以避開非原生字段算法,而非原生字段算法會增加成百上千倍的證明時間開銷。這些應用包括很多數字簽名(與區塊鏈輕客戶端和基于 SNARK 的橋接相關的主要工作)的證明,Plonk/Groth16/Nova/Honk 證明的聚合,以及 Verkle 樹認證路徑的證明。
我樂觀地認為,社區將關注基于 sum-check 的 PIOP 與 FRI-Binius 承諾方案的結合,將其作為在許多應用程序中執行 SNARK 的正確方法。即使發生這種情況,基于曲線的快速 SNARK 仍然有用,除非這個世界完全棄用橢圓曲線加密(例如,在社會完成從非量子安全的加密系統轉移之后)。
小結:
基于曲線的承諾與目前的所有其他 zkVM 相競爭(所有現存其他 zkVM 都已經使用哈希承諾方案處理小字段)。
在證明關于橢圓曲線的聲明時(至少在證明非原生字段算法沒有重大進展的情況下),人們會想要使用結合曲線的 Jolt。
作為一個純 zkVM,Jolt 和 Binius 相結合的承諾將比其他替代方案快很多,除非是證明關于曲線的聲明或小字段證明(在這種情況下,人們將使用結合曲線的 Jolt),否則人們將使用 Jolt 和 Binius 相結合的承諾方案。
在將證明發布到鏈上之前,基于橢圓曲線的 SNARK 將繼續用于壓縮證明大小和驗證者成本。在這種情況下,處理大字段的 zkVM 將發揮作用。即使在今天,人們認為基于哈希的 zkVM 項目實際上是使用在 BN254 曲線上定義的 zkVM 作為遞歸過程的一部分。
4、?預編譯和 zkVM 基準
關于預編譯及其在 zkVM 和基準測試中的作用已存在一些討論。在我進行解釋之前,先來解釋一下什么是預編譯應該會有所幫助,因為預編譯這個詞的含義在不同的上下文中有所不同。
(1)以太坊中的「預編譯」
在以太坊虛擬機(EVM)中,預編譯是一個經常執行的操作,并且受原生支持以提高效率。這就避免了通過冗長的 EVM 操作碼序列執行這些操作所帶來的大量開銷和過高的 gas 成本。
「EVM 預編譯」和「初始指令」(操作碼)之間的區別主要是語義上的區別。例如,Keccak 哈希函數是一個 EVM 操作碼,而 SHA-2 則是 EVM 預編譯。預編譯和操作碼都是經常執行的操作,以太坊出于相同的目的對它們提供原生支持:優化效率和 gas 成本。不可否認,預編譯是 EVM 的一部分,EVM 通常用于廣泛地描述以太坊執行環境,包含的不僅僅是操作碼。
如果 EVM 的功能與操作碼基本相同,為什么還要有預編譯呢?主要在于慣例問題。另一個可能的原因是,預編譯由相對復雜的操作組成,比如將來可能需要更改的加密原語,如果它們沒有分配操作碼,則將來更改起來會更容易一些。
(2)zkVM 設計中的「預編譯」
在 zkVM 設計中,預編譯是指針對特定函數(如 Keccak 或 SHA 哈希)或特定一組橢圓曲線操作的具有特殊用途的 SNARK。如今的 SNARK 預編譯通常是通過手動優化的約束系統來實現的(盡管隨著社區轉向基于 sum-check 的 SNARK,這些約束系統的性質以及它們被證明的方式將會改變)。
EVM 預編譯器 zkVM 預編譯之間具有深度相似性。在 Jolt 發布之前,zkVM 通過手動優化的約束系統實現初始指令,每個指令一個,就像它們實現預編譯一樣。所謂的 zkVM 預編譯和所謂的初始指令之間的區別純粹是語義上的。他們之間沒有實際的區別。
在 Jolt 中,我們使用 lookups 來實現初始指令,而不使用傳統的約束。但是選擇通過約束來實現一些初始指令并沒有什么大問題。(事實上,lookups 甚至可以被視為一種約束。)實際上,正如我之前說過的,一旦我們轉向 Binius 承諾方案,我們可能不得不使用傳統的約束來實現 RISC-V 的加法和乘法。
5、zkVM 基準測試
有了這些背景了解,下面我來談談我對預編譯的看法,因為它們與 zkVM 和基準測試有關。
首先,在沒有預編譯的情況下對各種 RISC-V zkVM 進行基準測試正是對 RISC-V zkVM 進行基準測試的意義。「zkVM」一詞是一個非正式的叫法,因此必然產生分歧,但在我看來,具有一個或多個預編譯的 RISC-V zkVM 不再是 RISC-V 的 zkVM:它是基于 RISC-V 的新指令集的 zkVM,將每個預編譯添加為初始指令。至少,添加到 zkVM 的每個預編譯都會削弱 zkVM 范式的價值主張——每添加一個電路都會增加潛在的 bug 表面積,并且現有程序將無法開箱即用地利用這些新的預編譯。
有些人還將 zkEVM 的 EVM 預編譯概念與 zkVM 的預編概念混為一談。但這是兩個截然不同的東西。雖然 zkEVM 的一些關鍵操作——比如 Merkle 哈希和數字簽名驗證——確實比初始的 RISC-V 指令更復雜,但這并不能改變 EVM 預編譯和初始 EVM 指令之間沒有功能差異這樣一個事實。zkEVM 必須支持 EVM 預編譯,以聲明與 EVM 對等。換句話說,不支持 EVM 預編譯的 zkEVM 不同于像 Jolt 這樣的 RISC-V zkVM,后者將使用預編譯擴展 RISC-V 以外的指令集。
另一個問題是如何選擇一組「公平」的函數來對 zkVM 進行基準測試。但是對于 RISC-V zkVM 來說,任何函數集都是公平的。Prover 時間幾乎完全取決于 RISC-V CPU 運行的周期數,原因有兩點。首先,prover 在「獲取 – 解碼 – 執行」循環的「執行」部分花費了一小部分時間。其次,不同的 RISC-V 指令,以及內存訪問,證明時間都高度相似。(在 Jolt 中,它們都是通過離線內存檢測技術來處理的。)
最后,如果使用預編譯,Jolt 的表現可能不會比其他替代方案差。事實上,我預計它會表現更好,因為基于 sum-check 的預編譯將是最快的,并且可以集成到 Jolt 中而沒有開銷,因為它專門使用了基于 sum-check 的 PIOP。在這一點上,有些人擔心使用橢圓曲線承諾方案的預編譯將比使用基于哈希方案的預編譯差很多。如今,Jolt 使用曲線,但這并不是必須的,我們一直對轉向 Binius 的計劃持開放態度。
6、關于基準的廣泛思考
我們進行基準測試的主要目標是確定不同證明系統的內在性能情況,在某種程度上,它們可以與它們的實現拆分開。這種方法使社區能夠理解并聚焦于設計高性能且安全的 SNARK 的正確技術。但是,當試圖比較兩個不同的 SNARK 時,數不盡的混淆因素往往導致不可能進行嚴絲合縫的對比。
工程方面的努力是這些混淆因素之中的一個,盡管社區中的許多人似乎持對立觀點。想法似乎是這樣的:如果一個項目添加了「特性」,比如針對特定硬件的預編譯或進行了優化,那么它應該在任何基準測試中都擁有「榮譽」表現。
兩種觀點都有其可取之處。但長遠來看,后一種觀點顯然站不住腳。新方法在任何基準測試中都將永遠處于劣勢,因為那些新方法沒有與舊項目可比的時間。這樣的觀點是對進步的阻礙。
隨著時間的推移,我預計基準測試相關的混淆因素將減少。隨著 SNARK 的開發工具的成熟,SNARK 獲得良好的性能所需的工程方面的工作量將變少。zkVM 的成本主要取決于周期數,而不是任何特定應用程序的特性,這是一個小小的奇跡(至少對于 RISC-V 如此)。如果人們關注約束系統的選擇(而不是今天的 R1CS、AIR、Plonkish 等碎片化狀態),針對約束系統的 SNARK 可能也會出現類似的情況,使用約束系統大小的簡單度量方法來代替周期數。
在此之前,很難在混雜因素控制不足和過度控制之間取得適當的平衡。分歧是不可避免的,而建設者們將必須提供任何一個基準背后的全部背景、細節信息和基本原理,以便社區能夠理解和探討。