來源:登鏈社區
爲工作程序員提供的 ZKP 教程介紹。
你知道爲什么斑馬有條紋嗎?一種理論是這是一種僞裝。當斑馬聚集在一起時,這使得獅子更難以區分它們的獵物。獅子必須將獵物從群體中隔離出來才能追捕它[^1]。
人類也喜歡在人群中隱藏。一個具體的例子是,當多個人在一個集體名稱下作爲一個整體行動時。《聯邦黨人文集》就是這樣創作的[^2]。 另一個例子是 Bourbaki,這是 1930 年代一群法國數學家的集體筆名。這導致了現代數學大部分內容的徹底重寫,重點在於嚴謹性和公理化方法[^3]。
在數字時代,假設你在一個群聊中,想要發送一條有爭議的信息。你想證明你是其中的一員,而不透露是哪一位。我們如何在數字領域使用密碼學來做到這一點?我們可以使用一種叫做 群籤名 的東西。
從傳統上講,群籤名在數學上相當復雜且難以實現。然而,使用零知識證明(ZKP),這個數學問題變成了一個簡單的編程任務。在本文結束時,你將能夠自己編寫群籤名。
介紹
這篇文章將向你展示如何從零开始編寫基本的零知識證明(ZKP)。
在學習新的技術棧時,我們希望盡快掌握編輯-構建-運行的循環。只有這樣,我們才能开始從自己的經驗中學習。
我們將首先讓你設置環境,編寫一個簡單的程序,執行所謂的可信設置,然後盡快生成和驗證證明。之後,我們將識別一些改進我們程序的方法,實施這些改進並進行測試。在此過程中,我們將建立一個更好的心理模型,以便在實踐中編程 ZKP。最後,你將熟悉(某種方式)從零开始編寫 ZKP。
我們將逐步構建一個簡單的籤名方案,你可以證明你發送了特定的消息。你將能夠理解這段代碼的作用及其原因:
# 克隆倉庫並運行准備腳本
git clone git@github.com:oskarth/zkintro-tutorial.git
cd zkintro-tutorial
# 在執行之前瀏覽此文件的內容
less ./scripts/prepare.sh
./scripts/prepare.sh
我們建議你瀏覽 ./scripts/prepare.sh
的內容,以查看這將安裝什么,或者如果你更喜歡手動安裝。執行後,你應該看到 Installation complete
並且沒有錯誤。
如果你遇到問題,請查看最新的官方文檔 這裏[7]。完成後,你應該安裝以下版本(或更高版本):
pragma circom 2.0.0;
template Multiplier2 () {
signal input a;
signal input b;
signal output c;
c <== a * b;
}
component main = Multiplier2();
這就是我們的特殊程序或 _電路_。 [^6] 按行分析:
pragma circom 2.0.0;
- 定義所使用的 Circom 版本template Multiplier()
- 模板是大多數編程語言中對象的等價物,是一種常見的抽象形式signal input a;
- 我們的第一個輸入,a
;輸入默認是私有的signal input b;
- 我們的第二個輸入,b
;同樣默認是私有的signal output b;
- 我們的輸出,c
;輸出始終是公共的c <== a * b;
- 這做了兩件事:將信號c
賦值 並 約束c
等於a
和b
的乘積component main = Multiplier2()
- 實例化我們的主組件
最重要的行是 c <== a * b;
。這是我們實際聲明約束的地方。這個表達式實際上是兩個的組合:<--
(賦值)和 ===
(等式約束)。 [^7] Circom 中的約束只能使用涉及常量、加法或乘法的操作。它強制要求方程的兩邊必須相等。 [^8]
關於約束
約束是如何工作的?在類似數獨的上下文中,我們可能會說一個約束是“一個介於 1 和 9 之間的數字”。然而,在 Circom 的上下文中,這不是一個單一的約束,而是我們必須使用一組更簡單的等式約束(===
)來表達的東西。 [^9]
爲什么會這樣?這與底層的數學原理有關。從根本上講,大多數 ZKP 使用 _算術電路_,它表示對 多項式 的計算。在處理多項式時,你可以輕松引入常量,將它們相加、相乘並檢查它們是否相等。 [^10] 其他操作必須用這些基本操作來表達。你不必詳細了解這一點才能編寫 ZKP,但了解底層發生的事情可能會很有用。 [^11]
我們可以將電路可視化如下:
構建我們的電路
供你參考,最終文件可以在 example1-solution.circom
中找到。有關語法的更多詳細信息,請參見 官方文檔[9]。
我們可以通過運行以下命令來編譯我們的電路:
這是調用 circom
創建 example1.r1cs
和 example1.wasm
文件的一個簡單包裝。你應該會看到類似以下內容:
{
"pi_a": ["15932[...]3948", "66284[...]7222", "1"],
"pi_b": [
["17667[...]0525", "13094[...]1600"],
["12020[...]5738", "10182[...]7650"],
["1", "0"]
],
"pi_c": ["18501[...]3969", "13175[...]3552", "1"],
"protocol": "groth16",
"curve": "bn128"
}
這以一些數學對象(三個橢圓曲线元素)pi_a
、pi_b
和 pi_c
的形式指定了證明。[^20] 它還包括有關協議(groth16
)和使用的 _curve_(bn128
,我們暫時忽略的數學實現細節)的元數據。這使得驗證者知道如何處理此證明以正確驗證。
請注意,證明是多么簡短;無論我們的特殊程序多么復雜,它的大小都只有這個。這展示了我們在 _友好的零知識證明介紹_[10] 中討論的 ZKP 的 succinctness 屬性。上述命令還輸出了我們的 _公共輸出_:
這是與我們的見證和電路對應的所有公共輸出的列表。在這種情況下,有一個公共輸出對應於 c
:33。[^21]
我們證明了什么?我們知道兩個祕密值 a
和 b
,它們的乘積是 33。這展示了我們在上一篇文章中討論的 隱私 屬性。
請注意,證明在孤立狀態下沒有用,它需要隨之而來的公共輸出。
驗證證明
接下來,讓我們驗證這個證明。運行:
just verify_proof example1
這需要驗證密鑰、公共輸出和證明。通過這些,我們能夠驗證證明。它應該打印“證明已驗證”。請注意,驗證者從未接觸到任何私有輸入。
如果我們更改輸出會發生什么?打开 example1/target/public.json
,將 33 更改爲 34,然後再次運行上述命令。
你會注意到證明不再被驗證。這是因爲我們的證明並沒有證明我們有兩個數字,其乘積是 34。
恭喜你,你現在已經編寫了你的第一個 ZKP 程序,進行了可信設置,生成了證明並最終驗證了它!
練習
ZKP 的兩個關鍵屬性是什么,它們意味着什么?
證明者的角色是什么,她需要什么輸入?驗證者呢?
解釋
c <== a * b;
這一行的作用。爲什么我們需要進行可信設置?我們如何使用其產物?
代碼:完成
example1
,直到你生成並驗證了一個證明。
第二次迭代
通過上述電路,我們證明了我們知道兩個(祕密)數字的乘積。這與 質因數分解 問題密切相關,這是許多密碼學的基礎。[^22] 這個想法是,如果你有一個非常大的數字,找到兩個質數使其乘積等於這個大數字是很困難的。相反,檢查兩個數字的乘積是否等於另一個數字是非常簡單的。[^23]
然而,我們的電路存在一個大問題。你能看到嗎?
我們可以輕松地將輸入更改爲“1”和“33”。也就是說,一個數字 c
始終是 1 和 c
的乘積。這一點並不令人印象深刻,對吧?
我們想要做的是添加另一個 _約束_,使得 a
或 b
不能等於 1。這樣,我們就被迫進行適當的整數因式分解。
我們如何添加這個約束,需要做哪些更改?
更新我們的電路
我們將爲這些更改使用 example2
文件夾。不幸的是,我們不能僅僅寫 a !== 1
,因爲這不是一個有效的約束。[^24] 它不是由常量、加法、乘法和等式檢查組成的。我們如何表達“某物不是”?
這並不是立即直觀的,這種類型的問題是編寫電路的藝術所在。發展這種技能需要時間,並超出了本初始教程的範圍;幸運的是,有許多好的資源可以參考。[^25]
不過,有一些常見的習語。基本的想法是使用 IsZero()
模板來檢查一個表達式是否等於零。它對真值輸出 1,對假值輸出 0。
使用真值表[^26] 來顯示可能的值通常是有幫助的。以下是 IsZero()
的真值表:
這是一個如此有用的構建塊,以至於它被包含在 Circom 的庫 circomlib
中。在 circomlib
中還有許多其他有用的組件。[^27]
我們可以通過創建一個 npm
項目(JavaScript)並將其作爲依賴項添加來包含它。在 example2
文件夾中,我們已經爲你完成了這一步。要導入相關模塊,我們在 example2.circom
的頂部添加以下行:
include "circomlib/circuits/comparators.circom";
使用 IsZero()
,我們可以檢查 a
或 b
是否等於 1。修改 example2.circom
文件,使其包含以下行:
just generate_proof example2
just verify_proof example2
它仍然按預期生成和驗證證明。
如果我們將 example2/input.json
的輸入更改爲 1
和 33
並嘗試運行上述命令,我們將看到一個斷言錯誤。也就是說,Circom 甚至不會讓我們生成證明,因爲輸入違反了我們的約束。
完整流程圖
現在我們已經經歷了整個流程兩次,讓我們退後一步,看看所有部分是如何結合在一起的。
希望事情开始變得有些明朗。接下來,讓我們提升一下,讓我們的電路更有用。
練習
爲什么我們必須運行
example2
的第 2 階段,而不是第 1 階段?上一個例子的主要問題是什么,我們是如何解決的?
代碼:完成
example2
,直到你無法生成證明。
第三次迭代
通過上述電路,我們已經證明了我們知道兩個祕密值的乘積。單靠這一點並不是很有用。在現實世界中,有用的是 _數字籤名方案_。通過它,你可以向其他人證明你寫了特定的消息。我們如何使用 ZKP 來實現這一點?要實現這一點,我們必須首先涵蓋一些基本概念。
現在是短暫休息的好時機,去喝一杯你最喜歡的飲料。
數字籤名
數字籤名已經存在,並且在我們的數字時代無處不在。現代互聯網沒有它們是無法運作的。通常,這些是使用 公鑰密碼學 實現的。在公鑰密碼學中,你有一個私鑰和一個公鑰。私鑰僅供你自己使用,而公鑰是公开共享的,代表你的身份。
數字籤名方案由以下部分組成:
密鑰生成:生成一個私鑰和相應的公鑰
籤名:使用私鑰和消息創建籤名
籤名驗證:驗證消息是否由相應的公鑰籤名
雖然具體細節看起來不同,但我們編寫的程序和上述密鑰生成算法共享一個共同元素:它們都使用 _單向函數_,更具體地說是 _陷門函數_。陷門是容易掉進去但難以爬出來的東西(除非你能找到一把隱藏的梯子) [^30]。
對於公鑰密碼學,從私鑰構造公鑰是容易的,但反過來卻非常困難。我們的前一個程序也是如此。如果這兩個祕密數字是非常大的質數,那么將該乘積轉回原始值是非常困難的。現代公鑰密碼學通常在底層使用 _橢圓曲线密碼學_。
傳統上,創建像這些數字籤名方案這樣的密碼協議需要大量的工作,並需要提出一個涉及一些巧妙數學的特定協議。我們不想這樣做。相反,我們想使用 ZKP 編寫一個程序,以實現相同的結果。
而不是這樣:[^31]
我們只想編寫一個程序,生成我們想要的證明,然後驗證這個證明。
哈希函數和承諾
我們將使用兩個更簡單的工具:_哈希函數_ 和 _承諾_,而不是使用橢圓曲线密碼學。
哈希函數也是一種單向函數。例如,在命令行中,我們可以這樣使用 SHA-256 哈希函數:
commitment = hash(some_secret)
signature = hash(some_secret, message)
此時你可能有一些問題。讓我們解決一些你腦海中可能存在的問題。
首先,爲什么這有效,我們爲什么需要 ZKP?當有人驗證證明時,他們只能訪問承諾、消息和籤名。沒有直接的方法可以驗證承諾是否對應於祕密,而不揭示祕密。在這種情況下,我們只是在生成證明時“揭示”祕密,因此我們的祕密保持安全。
其次,爲什么在 ZKP 內部使用這些哈希函數和承諾,而不是公鑰密碼學?你絕對可以在 ZKP 內部使用公鑰密碼學,並且這樣做是有合理理由的。就約束而言,它的實現成本遠高於上述方案。這使得它比上述更慢,更復雜。正如我們將在下一節中看到的,哈希函數的選擇非常重要。
最後,爲什么在我們已經擁有公鑰密碼學的情況下還要使用 ZKP?在這個簡單的例子中,沒有必要使用 ZKP。然而,它作爲更有趣的應用的構建塊,例如本文开頭提到的群籤名示例。畢竟,我們想要 _編程密碼學_。
這真是太多了!幸運的是,我們已經過了難關。讓我們开始編碼吧。如果你一开始沒有完全理解上述內容,也不用擔心。習慣這種推理方式需要一些時間。
回到代碼
我們將從 example3
目錄开始工作。
要實現數字籤名,我們需要做的第一件事是生成我們的密鑰。這些對應於公鑰密碼學中的私鑰和公鑰。由於密鑰對應於一個身份(你,證明者),我們將分別稱其爲 identity_secret
和 identity_commitment
。它們共同形成一個身份對。
這些將作爲電路的輸入,與我們要籤名的消息一起使用。作爲公共輸出,我們將擁有籤名、承諾和消息。這將允許某人驗證籤名確實是正確的。
由於我們需要身份對作爲電路的輸入,因此我們將單獨生成這些:just generate_identity
這會產生類似於以下內容:
include "circomlib/circuits/poseidon.circom";
Poseidon 哈希模板的使用如下:
component main {public [identity_commitment, message]} = SignMessage();
默認情況下,我們電路的所有輸入都是私有的。通過這個,我們明確標記 identity_commitment
和 message
爲公共。這意味着它們將成爲公共輸出的一部分。
有了這些信息,你應該有足夠的知識來完成 example3.circom
電路。如果你仍然卡住,可以參考 example3-solution.circom
獲取完整代碼。
像之前一樣,我們必須構建電路並運行受信任設置的第 2 階段:
{
"identity_secret": "21879[...]1709",
"identity_commitment": "48269[...]7915",
"message": "42"
}
隨意將身份對更改爲你自己使用 just generate_identity
生成的身份對。畢竟,你想把身份祕密保留給自己!
你可能會注意到消息只是一個作爲字符串引用的數字 ("42"
)。不幸的是,由於約束在數學上的工作方式(使用线性代數和 _算術電路_),我們只能使用數字而不能使用字符串。電路內部支持的唯一操作是基本的算術操作,如加法和乘法。[^37]
我們現在可以生成和驗證一個證明:
["48968[...]5499", "48269[...]7915", "42"]
這分別對應於籤名、承諾和消息。
讓我們看看如果我們不小心,事情可能會出錯。 [^38]
首先,如果我們將身份承諾更改爲 input.json
中的隨機內容,會發生什么?你會注意到我們無法再生成證明。這是因爲我們還在電路內部檢查身份承諾。保持身份祕密和承諾之間的關系至關重要。
其次,如果我們不將消息包含在輸出中,會發生什么?我們確實得到了一個證明,並且它得到了驗證。但消息可以是 _任何東西_,因此它實際上並不能證明你發送了特定的消息。類似地,如果我們不將身份承諾包含在公共輸出中,會發生什么?這意味着身份承諾可以是任何東西,因此我們實際上不知道 誰 籤署了消息。
作爲思考練習,想想如果我們省略這兩個關鍵約束中的任何一個會發生什么:
identity_commitment === identityHasher.out
signature <== signatureHasher.out
恭喜你,現在你知道如何編程加密了![^39]
練習
數字籤名方案的三個組成部分是什么?
使用像 Poseidon 這樣的 "ZK-Friendly hash function" 的目的是什么?
什么是承諾?我們如何將它們用於數字籤名方案?
爲什么我們將身份承諾和消息標記爲公共?
爲什么我們需要身份承諾和籤名約束?
代碼:完成
example3
,直到你生成並驗證了一個證明。
下一步
通過上述數字籤名方案,以及我們在文章中看到的一些技巧,你擁有了實現文章开頭提到的 群籤名方案 的所有工具。[^40]
在 example4
中存在骨架代碼。你只需要 5-10 行代碼。唯一的新語法是 for
循環,它的工作方式與大多數其他語言相同。[^41]。
這個電路將允許你:
籤署一條消息
證明你是三個人之一(身份承諾)
但不透露是哪一個
你可以把它看作一個謎題。關鍵的見解基本上歸結爲一個算術表達式。如果可以的話,嘗試在紙上解決它。如果你卡住了,可以像之前一樣查看解決方案。
最後,如果你想要一些額外的挑战,這裏有一些擴展的方法:
允許組內任意多的人
實現一個新的電路
reveal
,證明你籤署了特定的消息實現一個新的電路
deny
,證明你沒有籤署特定的消息
使用經典工具創建這樣的加密協議將是一項巨大的任務,需要大量的專業知識。[^42] 使用 ZKP,你可以在一個下午變得高效和危險,將這些問題視爲編程任務。這只是我們可以做的冰山一角。
練習
群籤名與普通籤名有什么不同?它們可以如何使用?
問題
這些問題是可選的,需要更多的努力。
找出
IsZero()
是如何實現的。代碼:完成上述群籤名方案(見
example4
)。代碼:擴展上述群籤名示例:允許更多人並實現
reveal
和/或deny
電路。你將如何設計一個 "ZK 身份" 系統來證明你已滿 18 歲?你可能想證明的其他屬性是什么?從高層次來看,你將如何實現它,以及你看到的挑战是什么?研究現有解決方案以更好地理解它們是如何實現的。
對於像以太坊這樣的公共區塊鏈,有時會使用 Layer 2 (L2) 來允許更快、更便宜和更多的交易。從高層次來看,你將如何使用 ZKP 設計一個 L2?解釋你看到的一些挑战。研究現有解決方案以更好地理解它們是如何實現的。## 結論
在本教程介紹中,我們熟悉了如何從頭开始編寫和修改基本的零知識證明(ZKPs)。我們設置了編程環境並編寫了一個基本電路。然後我們進行了可信設置,創建並驗證了證明。我們識別了一些問題並改進了電路,確保測試我們的更改。之後,我們使用哈希函數和承諾實現了一個基本的數字籤名方案。
我們還學習了足夠的技能和工具,以便能夠實現群體籤名,這在沒有零知識證明的情況下是很難實現的。
我希望你對編寫零知識證明所涉及的內容有了更好的心理模型,並對實際中的編輯-運行-調試周期有了更好的理解。這將爲你將來可能編寫的任何其他零知識證明程序打下良好的基礎,無論你最終使用什么技術棧。
致謝
感謝 Hanno Cornelius、Marc Köhlbrugge、Michelle Lai、lenilsonjr 和 Chih-Cheng Liang 閱讀草稿並提供反饋。
圖片
Bourbaki Congress 1938 - 未知,公有領域,通過 Wikimedia[11]
Hartmann's Zebras - J. Huber,CC BY-SA 2.0,通過 Wikimedia[12]
Trapdoor Spider - P.S. Foresman,公有領域,通過 [Wikimedia](https://commons.wikimedia.org/wiki/File:Trapdoor_(PSF\ "Wikimedia").png)
Kingsley Lockbox - P.S. Foresman,公有領域,通過 Wikimedia[13]
參考資料
[1] AI翻譯官: https://learnblockchain.cn/people/19584
[2] 翻譯小組: https://learnblockchain.cn/people/412
[3] learnblockchain.cn/article…: https://learnblockchain.cn/article/9178
[4] 零知識的友好介紹: https://learnblockchain.cn/article/6184
[5] git 倉庫: https://github.com/oskarth/zkintro-tutorial
[6]git 倉庫: https://github.com/oskarth/zkintro-tutorial
[7]這裏: https://docs.circom.io/getting-started/installation/
[8]zkrepl.dev: https://zkrepl.dev/
[9]官方文檔: https://docs.circom.io/circom-language/signals/
[10]友好的零知識證明介紹: https://learnblockchain.cn/article/6184
[11]Wikimedia: https://commons.wikimedia.org/wiki/File:Bourbaki_congress1938.png
[12]Wikimedia: https://commons.wikimedia.org/wiki/File:Hartmann_zebras_hobatereS.jpg
[13]Wikimedia: https://commons.wikimedia.org/wiki/File:Kingsley_lockbox.jpg
[14]AI 翻譯官: https://learnblockchain.cn/people/19584
[15]這裏: https://github.com/lbc-team/Pioneer/blob/master/translations/9178.md
[16]^2]: 參見 [聯邦黨人文集(維基百科): https://en.wikipedia.org/wiki/The_Federalist_Papers#Authorship
[17]^3]: 參見 [Bourbaki(維基百科): https://en.wikipedia.org/wiki/Nicolas_Bourbaki#Membership
[18]^8]: 這使得編寫約束相當具有挑战性,正如你可以想象的那樣。有關 Circom 中約束的更多詳細信息,請參見 [https://docs.circom.io/circom-language/constraint-generation/: https://docs.circom.io/circom-language/constraint-generation/
[19]^12]: 线性約束意味着它可以僅通過加法表示爲线性組合。這相當於使用常數進行乘法。需要注意的主要是线性約束比非线性約束更簡單。有關更多詳細信息,請參見 [約束生成: https://docs.circom.io/circom-language/constraint-generation/
[20]算術電路: https://docs.circom.io/background/background/#arithmetic-circuits
[21]^13]: 從數學上講,我們所做的是確保方程 Az * Bz = Cz
成立,其中 Z=(W,x,1)
。A
、B
和 C
是矩陣,W
是見證(私有輸入),x
是公共輸入/輸出。雖然知道這一點很有用,但編寫電路時並不需要理解這一點。有關更多詳細信息,請參見 [Rank-1 約束系統: https://docs.circom.io/background/background/#rank-1-constraint-system
[22]^15]: 正如在 友好的介紹 文章中提到的那樣,2016 年 Zcash 舉辦的儀式有一個很好的外行播客,你可以在 [這裏: https://radiolab.org/podcast/ceremony
[23]^17]: 我們稱之爲 1-out-of N 信任模型。還有許多其他信任模型;你最熟悉的可能是多數規則,即你信任大多數人做出正確的決定。這基本上就是民主和大多數投票的運作方式。 [↩: #user-content-fnref-17
[24]^22]: 也稱爲 _密碼學難度假設_。請參見 [計算難度假設 (維基百科): https://en.wikipedia.org/wiki/Computational_hardness_assumption#Common_cryptographic_hardness_assumptions
[25]^23]: 有關更多信息,請參見 [https://en.wikipedia.org/wiki/Integer_factorization: https://en.wikipedia.org/wiki/Integer_factorization
[26]^24]: 雖然我們可以添加 _asserts_,但這些實際上不是約束,僅用於清理輸入。有關其工作原理,請參見 [https://docs.circom.io/circom-language/code-quality/code-assertion/: https://docs.circom.io/circom-language/code-quality/code-assertion/
[27]https://www.chainsecurity.com/blog/circom-assertions-misconceptions-and-deceptions: https://www.chainsecurity.com/blog/circom-assertions-misconceptions-and-deceptions
[28]^25]: 這份由 0xPARC 提供的資源非常出色,如果你想深入了解編寫 (Circom) 電路的藝術: [https://learn.0xparc.org/materials/circom/learning-group-1/circom-1/: https://learn.0xparc.org/materials/circom/learning-group-1/circom-1/
[29]^26]: 由於編寫約束的性質,這種情況經常出現。請參見 [https://en.wikipedia.org/wiki/Truth_table: https://en.wikipedia.org/wiki/Truth_table
[30]^27]: 有關 circomlib 的更多信息,請參見 [https://github.com/iden3/circomlib: https://github.com/iden3/circomlib
[31]^28]: 請參見 [https://github.com/iden3/circomlib/blob/master/circuits/comparators.circom: https://github.com/iden3/circomlib/blob/master/circuits/comparators.circom
[32]^29]: 人們通常在項目之間共享這些 ptau
文件以提高安全性。有關詳細信息,請參見 [https://github.com/privacy-scaling-explorations/perpetualpowersoftau: https://github.com/privacy-scaling-explorations/perpetualpowersoftau
[33]https://github.com/iden3/snarkjs: https://github.com/iden3/snarkjs
[34]^30]: 這裏的梯子代表某種值,使我們能夠以相反的“困難”方式進行。另一種思考方式是將其視爲一個掛鎖。你可以輕松鎖定它,但很難解鎖,除非你有鑰匙。陷門函數也有更正式的定義,請參見 [https://en.wikipedia.org/wiki/Trapdoor_function: https://en.wikipedia.org/wiki/Trapdoor_function
[35]^31]: 來自維基百科的截圖。請參見 [ECDSA (維基百科): https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm#Signature_verification_algorithm
[36]^38]: 在現實世界的數字籤名方案中,當多個消息交換時,我們可能還希望引入一個加密隨機數。這是爲了避免重放攻擊,即某人可以在稍後時間重用相同的籤名。請參見 [https://en.wikipedia.org/wiki/Replay_attack: https://en.wikipedia.org/wiki/Replay_attack
[37]^40]: 在 ZKP 中實現群籤名的靈感來自 0xPARC,請參見 [https://0xparc.org/blog/zk-group-sigs: https://0xparc.org/blog/zk-group-sigs
[38]^41]: 請參見 [https://docs.circom.io/circom-language/control-flow/: https://docs.circom.io/circom-language/control-flow/
[39]^42]: 相比之下,實施群籤名的論文如 [https://eprint.iacr.org/2015/043.pdf: https://eprint.iacr.org/2015/043.pdf
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播信息之目的,不構成任何投資建議,如有侵權行為,請第一時間聯絡我們修改或刪除,多謝。