最精妙的ZK應用:回看Tornado Cash的原理與業務邏輯

新手2/28/2024, 5:40:33 AM
本文介紹Tornado爲代表的隱私項目,真正用到了ZK-SNARK算法的零知識性,而大多數打着ZK旗號的Rollup,用到的只是 ZK-SNARK的簡潔性。很多時候人們往往混淆了Validity Proof與ZK的區別,而Tornado恰好是理解ZK應用的極佳案例。

導語:近期Vitalik和一些學者聯名發表了新論文,其中提到了Tornado Cash如何實現反xi錢方案(其實就是讓取款人證明,自己的存款記錄屬於一個不包含黑錢的集合),但文中缺乏對Tornado Cash業務邏輯與原理的細致解讀,讓人似懂非懂。

此外值得一提的是,Tornado爲代表的隱私項目才是真正用到了ZK-SNARK算法的零知識性,而大多數打着ZK旗號的Rollup,用到的只是 ZK-SNARK的簡潔性。很多時候人們往往混淆了Validity Proof與ZK的區別,而Tornado恰好是理解ZK應用的極佳案例。本文作者恰好在2022年於Web3Caff Research寫過一篇關於Tornado原理的文章,今日將其部分段落節選並拓展,整理成文,以便大家系統的理解Tornado Cash。

原文連結:https://research.web3caff.com/zh/archives/2663?ref=157

“龍卷風”的原理

Tornado Cash是利用了零知識證明的混幣器協議,舊版本在2019年投入使用,新版本在2021年底啓動了beta版。Tornado舊版本基本實現了去中心化,鏈上合約開源且無多籤控制,前端代碼開源且備份在了FIL網路裏。由於舊版Tornado的整體結構更簡單易懂,所以本文將針對舊版本進行解讀。Tornado的主要思路是:把大量的存取款行爲混雜在一起,存款者在Tornado存入Token後,出示ZK Proof證明自己存過款,再用一個新地址提款,以此切斷存取款地址之間的關聯性。


更具體的概括,Tornado就像一個玻璃箱,混雜了很多人放進去的Coin硬幣。我們能看到放Coin的是哪些人,但這些Coin高度同質化,如果有生面孔的人從玻璃箱拿走一枚Coin,我們很難知道他拿走的Coin最初是誰放進去的。


(圖源:rareskills)這種場景似乎屢見不鮮:當我們從Uniswap池子裏SWAP幾枚ETH時,根本無法知道劃走的ETH是誰提供的,因爲曾給Uniswap提供流動性的人太多了。但不同之處是,每次用Uniswap劃走Token,我們需要用其他Token作爲等價的成本,且不能把資金“私密的”轉讓給別人;而混幣器只需要提款者出示存款憑證就行。爲了讓存取款動作看起來有同質性,Tornado池子的存款地址每次存入的資金、取款地址每次取出的資金都保持一致,比如某個池子的100個存款者和100名取款者,雖然公開可見,但看起來彼此沒有任何聯系,而且每人存入的金額、取出的金額,都是一樣的。這時就可以混淆視聽,沒法按照存取款金額判斷關聯性,進而切斷資金轉移痕跡,顯而易見的是,這爲xi錢行爲提供了天然的便利。


但有一個關鍵問題:取款者在提款時,怎麼證明自己存過款?向混幣器發起取款的地址,與所有的存款地址都不關聯,那麼該如何判斷他的提款資格?看起來最直接的方法,是取款者直接披露自己的存款記錄是哪一筆,但這就直接泄露了身分。此時零知識證明就派上了用場。提款者出具一個ZK Proof,證明自己在Tornado合約裏有存款記錄,且該筆存款尚未被提取,就能順利發起取款。零知識證明本身就實現了隱私保護,外界只知道:取款人的確往資金池裏存過款,但不知道他對應哪個存款者。


要證明“我在Tornado資金池裏存過款”可以被轉化爲“我的存款記錄可以在Tornado合約裏找到”。如果用Cn表示存款記錄,問題就歸納爲:已知Tornado的存款記錄集合爲{C1,C2,…C100…},取款者Bob證明自己曾用手上的密鑰,生成了存款記錄裏的某個Cn,但通過ZK不泄露Cn具體是哪個。這裏要用到Merkle Proof的特殊性質。因爲Tornado的所有存款記錄,都歸屬進了鏈上構造的一棵Merkle Tree,作爲其最底層的葉子結點,而葉子總數約爲2的20次冪>100萬,大多數都處於空白狀態(賦予了初始值)。每當有新存款行爲產生時,合約就會把其對應的特徵值Commitment記入一個葉子裏,然後更新Merkle Tree的root。


比如,Bob的存款操作是Tornado有史以來第1萬筆,那麼與這筆存款有關聯的一個特徵值Cn會寫入Merkle Tree的第1萬個葉子結點,也即C10000 = Cn。然後合約會自動算出新的Root,update一下。(ps:爲了節約計算量,Tornado合約會緩存之前一批有變化的節點的數據,比如下圖中的Fs1和Fs2、Fs0)


(圖源:RareSkills)而Merkle Proof本身很簡潔輕便,它利用了樹狀數據結構在檢索/溯源過程中的簡潔性。若想對外證明某筆交易TD存在於MerkleTree中,只要給出Root對應的MerkleProof(如下圖中右邊的部分),它相當簡潔。如果Merkle Tree格外龐大,底層葉子有2的20次冪個,也就是包含100萬筆存款記錄,Merkle Proof也只需要包含21個節點的數值,非常短。


如果要證明某筆交易H3的確包含在Merkle Tree中,設法證明用H3和Merkle Tree上其他的部分數據,可以生成Root,而生成Root所需要的那部分數據(包括Td在內)就構成了Merkle Proof。而Bob在取款時,要證明自己擁有的憑證對應着Merkle Tree上有記錄的某筆存款哈希Cn。也就是說,他要證明兩件事:·Cn存在於鏈上Tornado虛構的Merkle Tree中,具體可以構造一個Merkle Proof,裏面包含Cn;·Cn與Bob手上的存款憑證有關聯。

Tornado業務邏輯詳解

Tornado用戶界面的前端代碼中事先實現了很多功能,當一名存款者打開Tornado Cash網頁並點擊存款按鈕後,前端代碼附帶的程序會在本地生成2個隨機數K和r,隨後會計算出Cn=Hash(K,r)的值,再把Cn(就是下圖中的commitment)傳入Tornado合約,插入到後者記錄的Merkle Tree裏。說白了,K和r相當於私鑰。它們很重要,系統會提示用戶妥善保存。後面提款時仍然要用到K和r。


(此處的encryptedNote是可選項,允許用戶把憑證K和r用私鑰加密,存儲到鏈上,防止遺忘)值得注意的是,以上工作皆發生於鏈下,也就是說:Tornado合約和外界觀察者都不知曉K和r。如果K和r被泄露了,就類似於錢包私鑰被盜。


Tornado合約收到用戶存款,並收到用戶提交的Cn=Hash(K,r)後,便將Cn記入到Merkle樹的最底層,作爲新的葉子結點,同時會更新Root的數值。所以,Cn和用戶的存款動作是一對一關聯的,外界可以知道每個Cn對應着哪個用戶,知道有哪些人往混幣器裏存入了Token,並且知道每個存款者對應的存款記錄Cn。在取款步驟中,取款者在前端網頁裏輸入憑證/私鑰(存款時生成的隨機數K和r),Tornado Cash前端代碼中的程序會使用K和r、Cn=Hash(K,r)、Cn對應的Merkle Proof作爲輸入參數,生成ZK Proof,證明Cn是存在於Merkle Tree上的某筆存款記錄,而K和r是對應Cn的憑證。這一步就相當於證明:我知道某筆記錄於Merkle Tree上的存款記錄對應的密鑰。當ZK Proof被提交給Tornado合約時,上述4個參數均被隱藏,外界(包括Tornado合約)無法獲知,借此保障了隱私。生成ZKProof涉及的其他參數還包括:取款時Tornado合約裏記錄的Merkle root、自定義的收款地址A、防止重放攻擊的標識符nf(後面會講)。這3個參數會公開發布到鏈上,外界可以獲知,但不影響隱私。


這裏面有個細節,就是存款操作生成Cn時,用了2個隨機數K和r來生成Cn,而不是單個隨機數。這是因爲單個隨機數不夠安全,有一定概率發生碰撞,比如,採用單隨機數可能導致兩個不同的存款者恰巧採用1個同樣的隨機數,導致生成的Cn撞車。至於上圖中的A,代表接收提款的地址,由提款者自己填寫。nf則是一個防止重放攻擊的標識符,其數值nf=Hash(K),K就是存款生成Cn那一步用到的2個隨機數之一(K和r)。這樣一來,nf就與Cn關聯了起來,換言之,每個Cn都有對應的nf,兩者一一關聯。爲什麼要防止重放攻擊呢?由於混幣器在設計上的特性,取款時不知道用戶提走的幣對應Merkle樹的哪個葉子Cn,也就不知道提款人和哪些存款人關聯,就不知道提款人到底存過幾次款。提款者可以利用這一特性頻繁提款,發起重放攻擊,多次從混幣器池裏取走Token,直到把資金池抽幹。


在這裏,nf標識符的作用類似於每個以太坊地址都有的交易計數器nonce,都是爲了防止某筆交易被重放而設置。當一筆取款發生時,取款者需要提交一個nf,檢查這個nf是否已被使用過(記錄在案):如果有,此次取款無效。如果沒有,表示該nf尚未被使用,取款有效,對應的nf會被記錄下來。下次再有人提交這個nf時,對應的取款動作直接判定爲無效。


如果有人胡亂生成一個合約沒記錄過的nf行不行?當然不行,因爲取款者生成ZK Proof時,需要保證nf=Hash(K),而隨機數K與存款記錄Cn關聯,也就是說,nf與某筆有記錄的存款Cn關聯。如果隨便編造一個nf,這個nf與存款記錄中的所有存款都對不上號,就不能順利生成有效的ZK Proof,後續的工作就無法順利完成,取款操作就不會成功。可能也有人會問:不用nf行不行?既然提款者在提款時需要提交ZK證明,證明自己和某個Cn有關聯,那麼每當提款動作發生時,查找對應的ZK Proof是否被提交到鏈上過,不就行了嗎?但事實上,這樣做的成本很高,因爲Tornado cash合約不會永久存儲過去提交的ZK Proof,因爲這會嚴重浪費存儲空間。與其比較每個新交到鏈上的ZKProof和既有的Proof是否一致,還不如設置個佔地很小的標識符nf並將其永久存儲來的更劃算。按照取款函數的代碼示例,其需要的參數和業務邏輯如下:用戶提交ZKProof、nf(NullifierHash)=Hash(K),自定義一個接收提款的地址recipent,ZKProof隱藏了Cn和K、r的數值,讓外界無法獲取判斷用戶身分。recipent往往會填寫一個幹淨的新地址,也不會泄露個人信息。


但這裏面有個小問題,就是用戶在取款時,爲了不可溯源,往往用新申請的地址發起取款交易,此時新地址沒有ETH來支付gas費。所以取款地址發起取款時,要顯式聲明一個中繼者relayer,由它代付gas費,之後混幣器合約會直接從用戶提款裏扣掉一部分交給relayer,作爲回報。


綜上所述,TornadoCash可以隱瞞取款者與存款者的關聯,在用戶量很大的情況下,就如同一個鬧市區,犯人混進人羣後警方就難以追蹤。取款過程中需要用到ZK-SNARK,被隱藏起來的witness部分包含取款人關鍵信息,這是整個混幣器最關鍵的一點。目前看來,Tornado可能是與ZK相關的最巧妙的應用層項目之一。

聲明:

  1. 本文轉載自[極客 Web3],著作權歸屬原作者[Faust,極客web3],如對轉載有異議,請聯系Gate Learn團隊,團隊會根據相關流程盡速處理。
  2. 免責聲明:本文所表達的觀點和意見僅代表作者個人觀點,不構成任何投資建議。
  3. 文章其他語言版本由Gate Learn團隊翻譯, 在未提及Gate.io的情況下不得復制、傳播或抄襲經翻譯文章。

最精妙的ZK應用:回看Tornado Cash的原理與業務邏輯

新手2/28/2024, 5:40:33 AM
本文介紹Tornado爲代表的隱私項目,真正用到了ZK-SNARK算法的零知識性,而大多數打着ZK旗號的Rollup,用到的只是 ZK-SNARK的簡潔性。很多時候人們往往混淆了Validity Proof與ZK的區別,而Tornado恰好是理解ZK應用的極佳案例。

導語:近期Vitalik和一些學者聯名發表了新論文,其中提到了Tornado Cash如何實現反xi錢方案(其實就是讓取款人證明,自己的存款記錄屬於一個不包含黑錢的集合),但文中缺乏對Tornado Cash業務邏輯與原理的細致解讀,讓人似懂非懂。

此外值得一提的是,Tornado爲代表的隱私項目才是真正用到了ZK-SNARK算法的零知識性,而大多數打着ZK旗號的Rollup,用到的只是 ZK-SNARK的簡潔性。很多時候人們往往混淆了Validity Proof與ZK的區別,而Tornado恰好是理解ZK應用的極佳案例。本文作者恰好在2022年於Web3Caff Research寫過一篇關於Tornado原理的文章,今日將其部分段落節選並拓展,整理成文,以便大家系統的理解Tornado Cash。

原文連結:https://research.web3caff.com/zh/archives/2663?ref=157

“龍卷風”的原理

Tornado Cash是利用了零知識證明的混幣器協議,舊版本在2019年投入使用,新版本在2021年底啓動了beta版。Tornado舊版本基本實現了去中心化,鏈上合約開源且無多籤控制,前端代碼開源且備份在了FIL網路裏。由於舊版Tornado的整體結構更簡單易懂,所以本文將針對舊版本進行解讀。Tornado的主要思路是:把大量的存取款行爲混雜在一起,存款者在Tornado存入Token後,出示ZK Proof證明自己存過款,再用一個新地址提款,以此切斷存取款地址之間的關聯性。


更具體的概括,Tornado就像一個玻璃箱,混雜了很多人放進去的Coin硬幣。我們能看到放Coin的是哪些人,但這些Coin高度同質化,如果有生面孔的人從玻璃箱拿走一枚Coin,我們很難知道他拿走的Coin最初是誰放進去的。


(圖源:rareskills)這種場景似乎屢見不鮮:當我們從Uniswap池子裏SWAP幾枚ETH時,根本無法知道劃走的ETH是誰提供的,因爲曾給Uniswap提供流動性的人太多了。但不同之處是,每次用Uniswap劃走Token,我們需要用其他Token作爲等價的成本,且不能把資金“私密的”轉讓給別人;而混幣器只需要提款者出示存款憑證就行。爲了讓存取款動作看起來有同質性,Tornado池子的存款地址每次存入的資金、取款地址每次取出的資金都保持一致,比如某個池子的100個存款者和100名取款者,雖然公開可見,但看起來彼此沒有任何聯系,而且每人存入的金額、取出的金額,都是一樣的。這時就可以混淆視聽,沒法按照存取款金額判斷關聯性,進而切斷資金轉移痕跡,顯而易見的是,這爲xi錢行爲提供了天然的便利。


但有一個關鍵問題:取款者在提款時,怎麼證明自己存過款?向混幣器發起取款的地址,與所有的存款地址都不關聯,那麼該如何判斷他的提款資格?看起來最直接的方法,是取款者直接披露自己的存款記錄是哪一筆,但這就直接泄露了身分。此時零知識證明就派上了用場。提款者出具一個ZK Proof,證明自己在Tornado合約裏有存款記錄,且該筆存款尚未被提取,就能順利發起取款。零知識證明本身就實現了隱私保護,外界只知道:取款人的確往資金池裏存過款,但不知道他對應哪個存款者。


要證明“我在Tornado資金池裏存過款”可以被轉化爲“我的存款記錄可以在Tornado合約裏找到”。如果用Cn表示存款記錄,問題就歸納爲:已知Tornado的存款記錄集合爲{C1,C2,…C100…},取款者Bob證明自己曾用手上的密鑰,生成了存款記錄裏的某個Cn,但通過ZK不泄露Cn具體是哪個。這裏要用到Merkle Proof的特殊性質。因爲Tornado的所有存款記錄,都歸屬進了鏈上構造的一棵Merkle Tree,作爲其最底層的葉子結點,而葉子總數約爲2的20次冪>100萬,大多數都處於空白狀態(賦予了初始值)。每當有新存款行爲產生時,合約就會把其對應的特徵值Commitment記入一個葉子裏,然後更新Merkle Tree的root。


比如,Bob的存款操作是Tornado有史以來第1萬筆,那麼與這筆存款有關聯的一個特徵值Cn會寫入Merkle Tree的第1萬個葉子結點,也即C10000 = Cn。然後合約會自動算出新的Root,update一下。(ps:爲了節約計算量,Tornado合約會緩存之前一批有變化的節點的數據,比如下圖中的Fs1和Fs2、Fs0)


(圖源:RareSkills)而Merkle Proof本身很簡潔輕便,它利用了樹狀數據結構在檢索/溯源過程中的簡潔性。若想對外證明某筆交易TD存在於MerkleTree中,只要給出Root對應的MerkleProof(如下圖中右邊的部分),它相當簡潔。如果Merkle Tree格外龐大,底層葉子有2的20次冪個,也就是包含100萬筆存款記錄,Merkle Proof也只需要包含21個節點的數值,非常短。


如果要證明某筆交易H3的確包含在Merkle Tree中,設法證明用H3和Merkle Tree上其他的部分數據,可以生成Root,而生成Root所需要的那部分數據(包括Td在內)就構成了Merkle Proof。而Bob在取款時,要證明自己擁有的憑證對應着Merkle Tree上有記錄的某筆存款哈希Cn。也就是說,他要證明兩件事:·Cn存在於鏈上Tornado虛構的Merkle Tree中,具體可以構造一個Merkle Proof,裏面包含Cn;·Cn與Bob手上的存款憑證有關聯。

Tornado業務邏輯詳解

Tornado用戶界面的前端代碼中事先實現了很多功能,當一名存款者打開Tornado Cash網頁並點擊存款按鈕後,前端代碼附帶的程序會在本地生成2個隨機數K和r,隨後會計算出Cn=Hash(K,r)的值,再把Cn(就是下圖中的commitment)傳入Tornado合約,插入到後者記錄的Merkle Tree裏。說白了,K和r相當於私鑰。它們很重要,系統會提示用戶妥善保存。後面提款時仍然要用到K和r。


(此處的encryptedNote是可選項,允許用戶把憑證K和r用私鑰加密,存儲到鏈上,防止遺忘)值得注意的是,以上工作皆發生於鏈下,也就是說:Tornado合約和外界觀察者都不知曉K和r。如果K和r被泄露了,就類似於錢包私鑰被盜。


Tornado合約收到用戶存款,並收到用戶提交的Cn=Hash(K,r)後,便將Cn記入到Merkle樹的最底層,作爲新的葉子結點,同時會更新Root的數值。所以,Cn和用戶的存款動作是一對一關聯的,外界可以知道每個Cn對應着哪個用戶,知道有哪些人往混幣器裏存入了Token,並且知道每個存款者對應的存款記錄Cn。在取款步驟中,取款者在前端網頁裏輸入憑證/私鑰(存款時生成的隨機數K和r),Tornado Cash前端代碼中的程序會使用K和r、Cn=Hash(K,r)、Cn對應的Merkle Proof作爲輸入參數,生成ZK Proof,證明Cn是存在於Merkle Tree上的某筆存款記錄,而K和r是對應Cn的憑證。這一步就相當於證明:我知道某筆記錄於Merkle Tree上的存款記錄對應的密鑰。當ZK Proof被提交給Tornado合約時,上述4個參數均被隱藏,外界(包括Tornado合約)無法獲知,借此保障了隱私。生成ZKProof涉及的其他參數還包括:取款時Tornado合約裏記錄的Merkle root、自定義的收款地址A、防止重放攻擊的標識符nf(後面會講)。這3個參數會公開發布到鏈上,外界可以獲知,但不影響隱私。


這裏面有個細節,就是存款操作生成Cn時,用了2個隨機數K和r來生成Cn,而不是單個隨機數。這是因爲單個隨機數不夠安全,有一定概率發生碰撞,比如,採用單隨機數可能導致兩個不同的存款者恰巧採用1個同樣的隨機數,導致生成的Cn撞車。至於上圖中的A,代表接收提款的地址,由提款者自己填寫。nf則是一個防止重放攻擊的標識符,其數值nf=Hash(K),K就是存款生成Cn那一步用到的2個隨機數之一(K和r)。這樣一來,nf就與Cn關聯了起來,換言之,每個Cn都有對應的nf,兩者一一關聯。爲什麼要防止重放攻擊呢?由於混幣器在設計上的特性,取款時不知道用戶提走的幣對應Merkle樹的哪個葉子Cn,也就不知道提款人和哪些存款人關聯,就不知道提款人到底存過幾次款。提款者可以利用這一特性頻繁提款,發起重放攻擊,多次從混幣器池裏取走Token,直到把資金池抽幹。


在這裏,nf標識符的作用類似於每個以太坊地址都有的交易計數器nonce,都是爲了防止某筆交易被重放而設置。當一筆取款發生時,取款者需要提交一個nf,檢查這個nf是否已被使用過(記錄在案):如果有,此次取款無效。如果沒有,表示該nf尚未被使用,取款有效,對應的nf會被記錄下來。下次再有人提交這個nf時,對應的取款動作直接判定爲無效。


如果有人胡亂生成一個合約沒記錄過的nf行不行?當然不行,因爲取款者生成ZK Proof時,需要保證nf=Hash(K),而隨機數K與存款記錄Cn關聯,也就是說,nf與某筆有記錄的存款Cn關聯。如果隨便編造一個nf,這個nf與存款記錄中的所有存款都對不上號,就不能順利生成有效的ZK Proof,後續的工作就無法順利完成,取款操作就不會成功。可能也有人會問:不用nf行不行?既然提款者在提款時需要提交ZK證明,證明自己和某個Cn有關聯,那麼每當提款動作發生時,查找對應的ZK Proof是否被提交到鏈上過,不就行了嗎?但事實上,這樣做的成本很高,因爲Tornado cash合約不會永久存儲過去提交的ZK Proof,因爲這會嚴重浪費存儲空間。與其比較每個新交到鏈上的ZKProof和既有的Proof是否一致,還不如設置個佔地很小的標識符nf並將其永久存儲來的更劃算。按照取款函數的代碼示例,其需要的參數和業務邏輯如下:用戶提交ZKProof、nf(NullifierHash)=Hash(K),自定義一個接收提款的地址recipent,ZKProof隱藏了Cn和K、r的數值,讓外界無法獲取判斷用戶身分。recipent往往會填寫一個幹淨的新地址,也不會泄露個人信息。


但這裏面有個小問題,就是用戶在取款時,爲了不可溯源,往往用新申請的地址發起取款交易,此時新地址沒有ETH來支付gas費。所以取款地址發起取款時,要顯式聲明一個中繼者relayer,由它代付gas費,之後混幣器合約會直接從用戶提款裏扣掉一部分交給relayer,作爲回報。


綜上所述,TornadoCash可以隱瞞取款者與存款者的關聯,在用戶量很大的情況下,就如同一個鬧市區,犯人混進人羣後警方就難以追蹤。取款過程中需要用到ZK-SNARK,被隱藏起來的witness部分包含取款人關鍵信息,這是整個混幣器最關鍵的一點。目前看來,Tornado可能是與ZK相關的最巧妙的應用層項目之一。

聲明:

  1. 本文轉載自[極客 Web3],著作權歸屬原作者[Faust,極客web3],如對轉載有異議,請聯系Gate Learn團隊,團隊會根據相關流程盡速處理。
  2. 免責聲明:本文所表達的觀點和意見僅代表作者個人觀點,不構成任何投資建議。
  3. 文章其他語言版本由Gate Learn團隊翻譯, 在未提及Gate.io的情況下不得復制、傳播或抄襲經翻譯文章。
即刻開始交易
註冊並交易即可獲得
$100
和價值
$5500
理財體驗金獎勵!
It seems that you are attempting to access our services from a Restricted Location where Gate is unable to provide services. We apologize for any inconvenience this may cause. Currently, the Restricted Locations include but not limited to: the United States of America, Canada, Cambodia, Thailand, Cuba, Iran, North Korea and so on. For more information regarding the Restricted Locations, please refer to the User Agreement. Should you have any other questions, please contact our Customer Support Team.