zkSync專案實現了L2相關的方方面面。檢視zkSync的提交記錄,這個專案建立於2018年的11月份,已經有兩年的開發時間。從matter labs公開的各種專案,matter labs在zk rollup想的很遠,也已經走的蠻遠。細想想,踏踏實實地思考區塊鏈技術,認認真真地把一小點積累做好。整個原始碼目錄結構如下:
bin - 一些二進位制程式和腳步
contracts - 智慧合約邏輯
core - L2的核心邏輯
docker - docker相關的配置
docs - 文件,比較詳細的解釋了zkSync的協議設計,電路實現,智慧合約,K8如何配置,以及如何啟動等等。
etc - 配置資訊
js - zkSync客戶端以及瀏覽器(explorer)
zkSync的內容比較多,這篇文章主要給大家講講zkSync的大體邏輯結構,重點介紹智慧合約以及core的相關邏輯。所有zkSync的核心邏輯都實現在core的目錄下:
core/circuit - 零知識證明電路實現
core/crypto_exports - 零知識證明底層庫封裝
core/data_restore - 狀態恢復的相關邏輯
core/eth_client - eth客戶端的封裝
core/loadtest - 壓力測試相關邏輯
core/models - zkSync的模型定義
core/plasma - zkSync的狀態實現
core/prover - 零知識證明的證明生成引擎
core/server - 各種server的實現,包括eth的監測(watch)和交易傳送(sender),交易費用的計算(fee_ticker),零知識證明的伺服器(prover server)以及API服務等等
core/storage - 所有狀態的儲存以及持久化邏輯
2. 整體架構
zkSync目前主要實現了L2的轉賬功能。L2有自己的賬戶系統以及狀態。zkSync總體的功能模組以及相互之間的關係如下:
Eth Watch以及Eth Sender負責監控和傳送zkSync智慧合約的交易。Mem Pool負責收集交易。交易分為兩種兩種:L1交易和L2交易。Block Proposer將交易打包,並更改世界狀態(Plasma State)。在世界狀態更改後,透過Block Committer生成證明需要的資訊。零知識的證明透過Plonk證明系統生成,其中包括Prover Server和Proving Client。API提供介面實現UI的資訊展示和L2的交易提交。
3. L2賬戶系統和世界狀態
zkSync並不需要獨立生成新賬戶。zkSync的L2賬戶和L1賬戶一一對應,“共享”一份私鑰。
簡單的說,L1的私鑰的ECDSA簽名的結果作為L2賬戶的私鑰。L2賬戶的秘鑰是JubJub曲線上的Scalar值,對應的公鑰是對應橢圓曲線上的點。L2的狀態包括兩部分:賬戶以及賬戶下所有Token的餘額。
賬戶以及支援的Token個數定義在etc/env/dev.env檔案中:
ACCOUNT_TREE_DEPTH=32
BALANCE_TREE_DEPTH=11
目前支援2^32個L2賬戶,2^11個Token。每個L2賬戶都有一個唯一的編號,從零開始。編號0,預設為Validator的賬戶。Account節點包括如下資訊(其中PubKeyHash就是L2賬戶的公鑰資訊):
每個Token也都有唯一編號。Token節點包括如下資訊:
zkSync支援的Token資訊由L1智慧合約維護,具體邏輯可以檢視contracts/contracts/Governance.sol的addToken函式。
4. 交易型別和區塊
zkSync目前支援如下交易型別(操作型別):
Noop - 空操作(L2)
Transfer - 轉賬(L2)
Transfer to new - 轉賬給新賬戶(L2)
Withdraw (Partial Exit)- 轉出資金(L2)
Deposit -轉入資金(L1)
Change pubkey - 更改L2的公鑰資訊(L2)
Full exit - 退出,從Layer1發起的交易,退出L2(L1)
close操作 - 退出,從Layer2發起的交易(新版本已經廢除,L2)
L2的區塊資訊相對簡單,總體的結構如下:
L2的區塊資訊包括:新的世界狀態的樹根(new root hash),區塊交易費用接收賬戶以及多個交易資訊。交易資訊分為兩種:一種是L2的交易,從L2的角度看,這些才是“正常”交易(transaction);另外一種是從L1發起的交易(Deposit以及Full exit)。
從L1發起的交易,在程式碼中常常被標記為"Priority"交易。相對來說,從L1發起的交易,相對L2的交易優先順序確實更高一些。
以Deposit交易為例,介紹一下L2賬戶建立的流程:
在L2建立賬戶,其實涉及到兩個交易型別。Deposit是L1的Priority的交易。在Eth Watch監測到交易後,發起L2的交易,並在世界狀態新增狀態。在賬戶建立後,再透過Change pubkey更新L2的公鑰資訊。
5. 基本流程
在明確了賬戶系統以及狀態後,整個L2的打包以及更改世界狀態的流程還是比較清晰的:
一切從NewTx開始。透過API,建立好的Transaction新增到Mem Pool。詳細邏輯可以檢視core/server/src/mempool.rs的add_tx函式。Block Proposer每隔一段時間,檢視Mem Pool中的有效交易,並打包。詳細邏輯可以檢視core/server/src/block_proposer.rs的run_block_proposer_task函式。
區塊打包成功後,更新世界狀態,詳細邏輯可以檢視core/server/src/state_keeper.rs的execute_tx_batch。
更新了世界狀態後,要將最新的世界狀態進行證明,詳細邏輯可以檢視core/server/src/committer.rs的commit_block函式。
證明透過Eth Sender傳送給L1。
6. Plonk證明系統
zkSync採用Plonk零知識證明的演算法,證明L2的交易和狀態的正確性。Plonk零知識證明的演算法,和Groth26演算法不一樣,是Universal的零知識演算法。Plonk演算法的技術細節後面會單獨寫文章詳細介紹。zkSync設計了相對獨立的Plonk證明系統,示意如下:
Plonk證明系統涉及到三個功能模組:Block Committer,Prover Server以及Prover。這三個模組之間的資料傳輸透過Storage儲存模組。Block Committer將Block資訊存入Storage,Prover Server從Storage提取Block並生成證明需要的資訊,並再次存入Storage,Prover從儲存中檢視需要證明的資訊,生成零知識證明。
Prover Server和Prover之間的呼叫關係,示意如下:
詳細邏輯可以檢視core/server/src/prover_server以及core/prover目錄。
7. zkSync電路實現
zkSync在bellman的基礎上增加了Plonk零知識證明系統。zkSync的電路設計非常有趣,由於篇幅原因,後面會單獨介紹zkSync的電路實現原理。簡單的說,zkSync採用Chunk設計,將交易切分為多個Chunk,支援不同的區塊大小。看zkSync原始碼的小夥伴,在理解電路邏輯前,先理解好Chunk的邏輯。
8. 狀態確定以及L1智慧合約
zkSync的交易可以由Layer1或者Layer2的操作發起。簡單的說,zkSync的交易經歷三個狀態:1/Request 2/ Committed 3/ Verified。只有Verified的操作才是確定性狀態。從狀態的角度看,交易的流程如下圖所示:
某個交易在打包後,會將交易對應的Pub Data資訊提交到鏈上,交易進入Committed狀態。在交易對應的區塊提交證明後,改交易進入Verified的狀態,也就是確定性狀態。
L1智慧合約提供了commitBlock和verifyBlock介面實現Pub Data資訊以及證明資訊的提交:
function commitBlock(
uint32 _blockNumber,
uint32 _feeAccount,
bytes32[] calldata _newBlockInfo,
bytes calldata _publicData,
bytes calldata _ethWitness,
uint32[] calldata _ethWitnessSizes
)
function verifyBlock(uint32 _blockNumber, uint256[] calldata _proof, bytes calldata _withdrawalsData)
external nonReentrant
具體的邏輯,感興趣的小夥伴可以檢視contracts/contracts/ZkSync.sol原始碼。
總結:
zkSync透過zk Rollup協議,實現了L2的轉賬。zkSync專案非常完整,是學習L2非常好的參考專案。zkSync採用Plonk零知識證明演算法向L1證明狀態的正確性。Plonk演算法是Universal的零知識證明演算法,只需要一次可信設定。zkSync電路設計採用Chunk設計,支援不同的區塊大小。