EVM自2015年問世以來正在經(jīng)歷一場史詩級的ZK改造。這場大改造主要有兩個方向。
第一個方向就是所謂的zkVM賽道,該賽道項目致力于將Application的性能提升到最優(yōu),而與以太坊虛擬機(jī)的兼容性并不是首要考慮的問題。這里有兩個子方向,其一是做自己的DSL (Domain Specific Language),比如StarkWare正致力于推廣Cairo語言,推廣難度并不小。其二是目標(biāo)兼容現(xiàn)有的比較成熟的語言,比如RISC Zero致力于讓zkVM兼容C++/Rust。該賽道的難點(diǎn)在于因為引入了指令集ISA,導(dǎo)致最終輸出的約束更復(fù)雜。
第二個方向就是所謂的zkEVM賽道,該賽道項目致力于EVM Bytecode的兼容,即Bytecode級別及其以上的EVM代碼都通過ZkEVM產(chǎn)生對應(yīng)的零知識證明,這樣以來原生的Solidity以太坊開發(fā)者會可以無成本遷移至zkEVM。該賽道選手主要有Polygon zkEVM、Scroll、Taiko和Fox。該賽道的難點(diǎn)在于兼容EVM這樣一個并不適合封裝在ZK證明系統(tǒng)時產(chǎn)生的冗余成本。Fox經(jīng)歷長時間的思考與論證,終于找到了從根本上消減第一代zkEVM巨大冗余的那把鑰匙:“小表模式”zkEVM。
數(shù)據(jù)和證明電路是zkEVM生成證明的兩大核心要素。一方面,在zkEVM中,證明者需要所有交易涉及的數(shù)據(jù)以證明交易帶來的狀態(tài)轉(zhuǎn)移是正確的,而EVM中的數(shù)據(jù)量大且結(jié)構(gòu)復(fù)雜。因此,如何整理和組織證明所需的數(shù)據(jù)便是構(gòu)建一個高效的zkEVM需要仔細(xì)考慮的問題。另一方面,怎么通過一系列的電路約束高效地證明(或檢驗)計算執(zhí)行的有效性與正確性,則是保證zkEVM安全性的基礎(chǔ)。
我們首先談第二個問題,因為這是所有設(shè)計zkEVM的團(tuán)隊都需要考慮的問題,這個問題的本質(zhì)其實就是“我們到底要證明什么?”而目前大家對這個問題的思路都是相似的,由于一個交易(或其涉及的op-code)可能是多種多樣的,直接按順序證明每一步的操作帶來的狀態(tài)改變都是正確的顯得不現(xiàn)實,因此我們需要分類證明。
例如,我們將每次stack中元素的變化都放在一塊,專門編寫一個stack電路證明,為單純的算術(shù)操作專門編寫一套的算術(shù)電路等等。如此一來,每個電路需要考慮的情況就變得相對簡單。這些不同功能的電路在不同zkEVM中有不同的名字,有人直接稱其為電路,也有人稱其為(子)狀態(tài)機(jī),但是這個思想的本質(zhì)都是一樣的。
為了更清楚的解釋這么做的意義,我們舉一個例子,假設(shè)現(xiàn)在要證明加法操作(將stack的上2個元素取出來,并將他們的和放回stack頂端):
假設(shè)原先的stack是[1,3,5,4,2]
則如果不分類拆分的話,我們需要設(shè)法證明進(jìn)行完上述操作后stack變?yōu)閇1,3,5,6]
而如果進(jìn)行了分類拆分的話我們只需要分別證明以下幾件事:
stack電路:
C1:證明[1,3,5,4,2] pop出2和4后變?yōu)閇1,3,5]
C2:證明[1,3,5] push(6) 后變?yōu)閇1,3,5,6]
算術(shù)電路:
C3:a=2,b=4,c=6,證明a+b=c
值得注意的是,證明的復(fù)雜程度和電路需要考慮的各種情況的數(shù)量有關(guān)系,如果不分類拆分的話,電路需要覆蓋的可能性將會非常巨大。
而一旦分類拆分了,每一個部分的情況將會變得相對單純,從而證明的難度也會顯著減小。
但是分類拆分也會帶來其他問題,那便是不同類別電路的數(shù)據(jù)一致性問題,例如在上面的例子里,我們實際上還需要證明以下兩件事:
C4:”C1中pop出來的數(shù)” = “C3中的a和b”
C5:“C2中push的數(shù)” = “C3中的c”
為了解決這個問題,我們回到了第一個問題,即我們要如何組織交易涉及的數(shù)據(jù),下面我們接著探討這個議題:
一個直觀的方法是這樣的:通過trace,我們可以拆解出所有交易涉及的每個步驟,知道其涉及的數(shù)據(jù),并通過向節(jié)點(diǎn)發(fā)送請求以獲得不在trace中的那部分?jǐn)?shù)據(jù),隨后,我們將其如下排列成一個大表格T:
“第一步操作” “第一步操作涉及的數(shù)據(jù)”
“第二步操作” “第二步操作涉及的數(shù)據(jù)”
…
“第n步操作” “第n步操作涉及的數(shù)據(jù)”
如此一來,在上面的例子中,我們就會有一行記錄著
“第k步:加法” “a=2, b=4, c=6”
而上面的C4便可以被如下證明:
C4(a):C1 pop 出的數(shù)和大表T中的第k步一致
C4(a):C3 的a和b和大表T中的第k步一致
C5也是類似的。這個操作(證明一些元素在一個表中出現(xiàn)過)被稱為lookup。lookup的具體算法我們不在本文中詳細(xì)介紹,但是可以想象,lookup操作的復(fù)雜度與大表T的大小密切相關(guān)。因此,現(xiàn)在我們回到第一個問題:如何組織證明會用到的數(shù)據(jù)呢?
我們考慮如下一系列的表格構(gòu)造:
表格Ta:
“類型a的第一個操作” “類型a的第一個操作涉及的數(shù)據(jù)”
“類型a的第二個操作” “類型a的第二個操作涉及的數(shù)據(jù)”
…
“類型a的第m個操作” “類型a的第m個操作涉及的數(shù)據(jù)”
表格Tb:
“類型b的第一個操作” “類型b的第一個操作涉及的數(shù)據(jù)”
“類型b的第二個操作” “類型b的第二個操作涉及的數(shù)據(jù)”
…
“類型b的第m個操作” “類型b的第n個操作涉及的數(shù)據(jù)”
…
如此構(gòu)造多個小表,這么做的好處是當(dāng)我們可以根據(jù)需要的數(shù)據(jù)所涉及的操作的類型,直接在對應(yīng)的小表中進(jìn)行l(wèi)ookup,如此一來,便能很大程度的提高效率。
一個簡單的例子(假設(shè)我們每次只能lookup一個元素)是如果我們要證明a~h這8個字母都存在[a,b,c,d,e,f,g,h]中,我們需要對大小為8的表進(jìn)行8次的lookup,但是如果我們把表分為[a,b,c,d]和[e,f,g,h]的話,我們只需要對這兩個大小為4的表分別進(jìn)行4次lookup就可以了!
在FOX這個layer2的zkEVM中便使用了這種小表的設(shè)計以提升效率,為了保證在各種情況下都能完備的證明,對于具體的小表拆分方式需要仔細(xì)的設(shè)計,而提升效率的關(guān)鍵則在于對表的內(nèi)容的分類與其大小的平衡。盡管將完整的zkEVM在這個框架中實現(xiàn)需要龐大的工作量,我們預(yù)期這樣的zkEVM將會在性能方面有突破性的進(jìn)步。
結(jié)論:Fox所發(fā)明的“小表模式”zkEVM,在保證原生的Solidity以太坊開發(fā)者能無成本遷移至zkEVM的同時,大幅削減封裝EVM到ZK證明系統(tǒng)時產(chǎn)生的冗余成本。這是zkEVM結(jié)構(gòu)的一次重大變革,將對以太坊擴(kuò)容方案產(chǎn)生深遠(yuǎn)影響。