作廢符
作廢符是一個巧妙的技巧,讓屏蔽系統能在不洩漏「花了什麼」的前提下防止雙重支付。在透明鏈上,你藉由從人人可見的集合中刪除一個 UTXO 來阻止雙重支付。但在私密池中,幣是隱藏的承諾——你無法在不破壞隱私下指出正被花用的那一枚。作廢符解決了這點:當你花用一張屏蔽票據時,你公佈一個由它推導出的唯一標籤,網路只需記錄它見過的每一個作廢符。一張票據只能被花一次,因為花兩次會兩度公佈相同的作廢符,而那會被拒絕。
深層的性質是:作廢符由票據確定性地產生,卻無法與票據連結。它以一個由擁有者秘密作金鑰的偽隨機函式計算——在 Zcash Sapling 中大致為 nf = PRF(nk, rho),其中 nk 是作廢符推導金鑰、rho 是每張票據的值——於是同一張票據永遠產生同一個作廢符(抓住雙重支付),但沒人能把公佈的作廢符連回它作廢的是哪個票據承諾,因為那需要擁有者的秘密金鑰。隨花費附上的零知識證明,使網路確信所揭露的作廢符是由承諾樹中某張有效、未花用的票據正確推導而來,卻不揭露是哪一張。
這套「承諾加作廢符」模式是零知識付款與混合系統的通用引擎。Tornado Cash 採用相同形狀:一筆存款把承諾插入 Merkle 樹,而一筆提領揭露一個作廢符雜湊與一個 SNARK,證明該作廢符對應樹中某個承諾,讓一個不相關的地址在不連結到存款下提領。合約儲存已花費的作廢符以阻擋重複提領。兩半是對稱的:承諾是進去的幣,作廢符是出來的幣。
一個微妙但重要的要點:作廢符必須以抗碰撞、且攻擊者無法暴力試湊的方式依賴於票據。若兩張不同票據可能產生相同作廢符,一筆誠實的花費就可能被阻擋(一種阻斷服務或「作廢符碰撞」漏洞);若作廢符洩漏關於票據的資訊,隱私便失敗。把這個 PRF 與其金鑰推導做到完全正確,是任何屏蔽協定中最攸關安全的部分之一。
commitment = hash(nullifier, secret) // deposit nullifierHash = hash(nullifier) // withdraw, recorded as spent
承諾進去、作廢符出來;兩者間的連結被 SNARK 隱藏。
承諾擴大匿名集;作廢符每筆花費恰好把它縮小一。作廢符集合是公開且僅可追加的,但它只是一串不透明標籤——它揭露發生了多少筆花費,卻從不揭露它們消耗了哪些票據。