用 Circom 寫出我的第一個零知識電路

現在的加密貨幣市場跌得很慘 📉。但說實話?熊市是我最喜歡學習的時候。沒有 FOMO,沒有 Twitter 的喧囂,只有我、筆電和一份超長的閱讀清單。

我已經聊了好一陣子零知識證明的話題了(可以看看我之前關於 STARK 的文章)。這次我終於不只是讀,而是從頭寫了一個實際的電路。

為什麼選 Circom?

我選擇從 Circom 入手,因為它是為 Solidity 開發者設計的,而且配套工具(snarkjs)的文件相當完整。雖然也有 Noir、Halo2 等選擇,但對於剛入門的我,Circom 感覺是最容易上手的起點。

讓我終於搞懂的心智模型是這樣的:ZK 電路就像一個函式,你可以證明自己知道產生某個特定輸出的輸入值,卻不需要透露那些輸入值本身。驗證者只知道「這個人確實知道答案」,但完全不知道答案是什麼。

我的第一個電路

我從經典範例開始:證明你知道兩個數字 ab,它們相乘等於公開的值 c。聽起來很簡單,但親手看到它跑起來之後,一切都變得具體了。

pragma circom 2.0.0;

template Multiply() {
    signal input a;
    signal input b;
    signal output c;

    c <== a * b;
}

component main = Multiply();

用 Circom 編譯之後,透過 snarkjs 生成證明金鑰與驗證金鑰,最後把 Solidity 驗證合約部署到測試網——成功了。驗證者確認了我的證明,但完全不知道 ab 是什麼。

第一次看到這個運作的時候,真的有點神奇 🤯

讓我踩到坑的地方

最難的部分是搞清楚 Circom 中「信號(signal)」和「變數(variable)」的差異。信號是受約束的,構成電路的一部分;變數則不受約束。如果搞混了,會出現很難察覺的錯誤——程式能編譯通過,但產生的證明是錯的。

另外,snarkjs 的可信設置(trusted setup)階段需要下載相當大的檔案(Powers of Tau 儀式的輸出)。文件有提醒,但我還是坐在那邊想說網路為什麼突然這麼慢。

接下來的方向

我想用這個建構一些實際有用的東西——大概是個簡單的私密投票電路。另外也計畫探索 Aztec 的 Noir,它的語法有點像 Rust,對我來說可能更自然。

ZK 很難,但這種難感覺是值得的。

參考資源