零知識證明Learn by Coding:libsnark入門篇
安比(SECBIT)实验室
2020-01-03 10:19
本文约17557字,阅读全文需要约70分钟
希望通過本系列文章,所有開發者都能親自上手實踐,在短時間內迅速入門libsnark,一步步了解libsnark 的基本概念。

本文作者:p0n1@安比實驗室

libsnark 是目前實現zk-SNARKs 電路最重要的框架,在眾多私密交易或隱私計算相關項目間廣泛應用,其中最著名當然要數Zcash。 Zcash 在Sapling 版本升級前一直使用libsnark 來實現電路(之後才替換為bellman)。毫不誇張地說,libsnark 支撐並促進了zk-SNARKs 技術的首次大規模應用,填補了零知識證明技術從最新理論到工程實現間的空缺。

希望通過本系列文章,所有開發者都能親自上手實踐,在短時間內迅速入門libsnark,一步步了解libsnark 的基本概念,學會如何開發zk-SNARKs 電路,完成證明的生成和驗證,最終將零知識證明應用到真實業務中去。

1. zk-SNARKs 和libsnark 背景簡介

零知識證明,可能是目前最具應用前景和想像力的密碼學黑科技。而zk-SNARKs 正是一類零知識證明方案的簡稱,全稱為Zero-Knowledge Succinct Non-interactive Arguments of Knowledge。這一名字幾乎包含了其所有技術特徵,即可以在不洩露任何其他信息的前提下證明一個命題的正確性,並且最終生成的證明具有簡潔性(Succinct),也就是說最終生成的證明足夠小,並且與計算量大小無關,是一個常數。用白話說就是,你理論上可以在不暴露任何隱私的情況下向其他所有人證明某件事,並且生成的證明體積很小,校驗成本很低,與需要證明的內容計算量無關。聽起來簡直太美好了!

zk-SNARKs 能應用到很多場景,比如隱私保護、區塊鏈擴容、可驗證計算等。本文不介紹zk-SNARKS 和零知識證明的理論細節,不熟悉或想深入了解的同學可閱讀其他文章或論文。

如Vitalik 寫的關於zk-SNARKs 著名的三篇博文。

或者閱讀向程@HUST寫的「深入淺出零知識證明之zk-SNARKs」,還有東澤寫的「淺談零知識證明之二:簡短無交互證明(SNARK)」

「從零開始學習zk-SNARK」系列,以及從安比實驗室維護的「零知識證明學習資源匯總」中查找更多資料。

本文主角libsnark 是用於開發zk-SNARKs 應用的C++ 代碼庫,由SCIPR Lab 開發並維護。 libsnark 工程實現背後的理論基礎是近年來(尤其是2013 年以來)零知識證明特別是zk-SNARKs 方向的一系列重要論文。如以下最著名的數篇:

  • [GGPR13] Quadratic span programs and succinct NIZKs without PCPs , Rosario Gennaro, Craig Gentry, Bryan Parno, Mariana Raykova, EUROCRYPT 2013

  • [PGHR13] Pinocchio: Nearly Practical Verifiable Computation , Bryan Parno, Craig Gentry, Jon Howell, Mariana Raykova, IEEE Symposium on Security and Privacy (Oakland) 2013

  • [BCGTV13] SNARKs for C: Verifying Program Executions Succinctly and in Zero Knowledge , Eli Ben-Sasson, Alessandro Chiesa, Daniel Genkin, Eran Tromer, Madars Virza, CRYPTO 2013

  • [BCIOP13] Succinct non-interactive arguments via linear interactive Proofs , Nir Bitansky, Alessandro Chiesa, Yuval Ishai, Rafail Ostrovsky, Omer Paneth, Theory of Cryptography Conference 2013

  • [BCTV14a] Succinct non-interactive zero knowledge for a von Neumann architecture , Eli Ben-Sasson, Alessandro Chiesa, Eran Tromer, Madars Virza, USENIX Security 2014

  • [BCTV14b] Scalable succinct non-interactive arguments via cycles of elliptic curves , Eli Ben-Sasson, Alessandro Chiesa, Eran Tromer, Madars Virza, CRYPTO 2014

  • [Groth16] On the Size of Pairing-based Non-interactive Arguments , Jens Groth, EUROCRYPT 2016

libsnark 的開發者們亦是這個領域內頂尖的學者或研究牛人,如Eran Tromer 更是以上多篇論文的共同作者。

紮實的理論基礎和工程能力,讓libsnark 的作者們能夠化繁為簡,將形如下圖的高深理論和復雜公式逐一實現,高度工程化地抽像出簡潔的接口供廣大開發者方便地調用。向這些將非凡的理論研究推廣至更大規模應用的先鋒們致敬。

下圖是libsnark 的模塊總覽圖,摘自libsnark 代碼貢獻量第一作者Madars Virza 在MIT 的博士論文

)

其中:

其中:

其中:

  • zk_proof_systems/ppzksnark/r1cs_ppzksnark 對應的是BCTV14a

  • zk_proof_systems/ppzksnark/r1cs_gg_ppzksnark 對應的是Groth16

如果想研究這兩個協議的實現細節可直接從這兩個目錄入手。 ppzksnark 是指preprocessing zkSNARK。這裡的pp/preprocessing 其實就是指我們常說的trusted setup,即在證明生成和驗證之前,需要通過一個生成算法來創建相關的公共參數(proving key 和verification key)。我們也把這個提前生成的參數稱為「公共參考串」(Common Reference String),或簡稱為CRS

2. 基本原理與步驟

利用libsnark 庫開發zk-SNARKs 應用從原理上可簡要概括為以下四個步驟:

  • 將待證明的命題表達為R1CS (Rank One Constraint System)

  • 使用生成算法(G)為該命題生成公共參數

  • 使用證明算法(P)生成R1CS 可滿足性的證明

  • 這篇文章

這篇文章這篇文章

有這樣一個函數C(x, out),用於判斷秘密x 是否滿足等式x^3 + x + 5 == out,若滿足則返回true。

function C(x, out) {
 return ( x^3 + x + 5 == out );
}

第一步,我們需要將函數C(x, out) 在libsnark 中進行表達。此處先省略,後面介紹詳細過程。

第二步,對應下面的Generator 函數(G),lambda 為隨機產生,也就是常說的trusted setup 過程中產生的"toxic waste"。人們喜歡稱它為“有毒廢物”,是因為它必須被妥善處理(如必須銷毀,不能讓任何人知道),否則會影響證明協議安全。

lambda <- random()

(pk, vk) = G(C, lambda)

最終生成proving key (pk) 和verification key (vk)。

第三步,對應使用Prove 函數(P)生成證明。這裡想證明的是prover 知道一個秘密值x 和計算結果out 可使等式滿足。因此將x、out 還有pk 作為輸入一起傳給P,最終生成證明proof。

proof = P(pk, out, x)

第四步,對應使用Verify 函數(V)驗證證明,將proof、out 還有vk 傳給G,即可在不暴露秘密的情況下證明存在一個秘密值可使等式滿足。

V(vk, out, proof) ?= true

而開發者主要工作量就集中在第一步,需要按照libsnark 的接口規則手寫C++ 電路代碼來描述命題,由代碼構造R1CS 約束。整個過程也即對應下圖的Computation -> Arithmetic Circuit -> R1CS。

3. 搭建zk-SNARKs 應用開發環境

下面進入動手環節,快速上手libsnark,跑通例子。

先下載本文對應的libsnark 最小可用例子代碼庫libsnark_abc。

git clone https://github.com/sec-bit/libsnark_abc.git

通過git submodule 拉取libsnark 代碼。

cd libsnark_abc
git submodule update --init --recursive

參考libsnark項目文檔完成相關依賴安裝。以Ubuntu 16.04 LTS 為例,需安裝以下組件:

sudo apt-get install build-essential cmake git libgmp3-dev libprocps4-dev python-markdown libboost-all-dev libssl-dev

初始化build 文件夾。

mkdir build && cd build && cmake ..

這步在macOS 系統可能會遇到問題,參考這個issue處理。或嘗試使用以下命令:

mkdir build && cd build && CPPFLAGS=-I/usr/local/opt/openssl/include LDFLAGS=-L/usr/local/opt/openssl/lib PKG_CONFIG_PATH=/usr/local/opt/openssl/lib/pkgconfig cmake -DWITH_PROCPS=OFF -DWITH_SUPERCOP=OFF ..

成功後,依舊在build 目錄進行編譯。

make

編譯成功後,在build/src 目錄中可看到3 個二進製文件。


main
range
test

到這兒,你就以及完成示例項目的編譯啦。嘗試運行示例代碼吧。


./src/main

最終出現如下日誌,則說明一切正常。你已順利擁有了zkSNARK 應用開發環境,並成功跑了第一個zk-SNARKs 的demo。

4. 理解示例代碼

下面我們一起來仔細瞅瞅代碼。示例項目包含了3 份代碼(也可查看文末附錄)。

不妨先看看src/main.cpp。這個例子來自Howard Wu 的libsnark_tutorial,他也是libsnark 作者之一哦。本文libsnark_abc 的項目結構就是依照他的libsnark_tutorial 搭建,屬於“官方推薦風格” ,請放心食用😆。

只有區區幾十行代碼,其中run_r1cs_gg_ppzksnark() 是主要部分。很容易發現,真正起作用的實質代碼只有下面5 行。


r1cs_gg_ppzksnark_keypair keypair = r1cs_gg_ppzksnark_generator(example.constraint_system);

r1cs_gg_ppzksnark_processed_verification_key pvk = r1cs_gg_ppzksnark_verifier_process_vk(keypair.vk);

r1cs_gg_ppzksnark_proof proof = r1cs_gg_ppzksnark_prover(keypair.pk, example.primary_input, example.auxiliary_input);

const bool ans = r1cs_gg_ppzksnark_verifier_strong_IC(keypair.vk, example.primary_input, proof);

const bool ans2 = r1cs_gg_ppzksnark_online_verifier_strong_IC(pvk, example.primary_input, proof);

僅從“超長”的函數名就能看出來每步是在做什麼,但是卻看不到如何構造電路的細節。實際上這裡僅僅是調用了自帶的r1cs_example,隱去了實現細節。

既然如此,那讓我們通過一個更直觀的例子來學習電路細節。研究src/test.cpp,這個例子改編自Christian Lundkvist 的libsnark-tutorial

代碼開頭僅引用了三個頭文件,分別是:


#include
#include
#include

前面提到r1cs_gg_ppzksnark 對應的是Groth16 方案。這裡加了gg 是為了區別r1cs_ppzksnark(也就是BCTV14a 方案),表示Generic Group Model(通用群模型)。 Groth16 安全性證明依賴Generic Group Model,以更強的安全假設換得了更好的性能和更短的證明。

第一個頭文件是為了引入default_r1cs_gg_ppzksnark_pp 類型,第二個則為了引入證明相關的各個接口。 pb_variable 則是用來定義電路相關的變量。

下面需要進行一些初始化,定義使用的有限域,並初始化曲線參數。這是相當於每次的準備工作。


typedef libff::Fr FieldT;
default_r1cs_gg_ppzksnark_pp::init_public_params();

接下來就需要明確「待證命題」是什麼。這裡不妨沿用之前的例子,證明秘密x 滿足等式x^3 + x + 5 == out。這實際也是Vitalik 博文"Quadratic Arithmetic Programs: from Zero to Hero"中用的例子。如果對下面的變化陌生,可嘗試閱讀該博文。

通過引入中間變量sym_1、y、sym_2 將x^3 + x + 5 = out 扁平化為若干個二次方程式,幾個只涉及簡單乘法或加法的式子,對應到算術電路中就是乘法門和加法門。你可以很容易地在紙上畫出對應的電路。


x * x = sym_1
sym_1 * x = y
y + x = sym_2
sym_2 + 5 = out

通常文章到這里便會順著介紹如何按照R1CS 的形式編排上面的幾個等式,並一步步推導出具體對應的向量。這對理解如何把Gate 轉換為R1CS 有幫助,然而卻不是本文的核心目的。所以此處省略一百字。

下面定義與命題相關的變量。首先創建的protoboard 是libsnark 中的一個重要概念,顧名思義就是原型板或者麵包板,用來快速搭建電路,在zk-SNARKs 電路中則是用來關聯所有變量、組件和約束。接下來的代碼定義了所有需要外部輸入的變量以及中間變量。


// Create protoboard
protoboard pb;

// Define variables
pb_variable x;
pb_variable sym_1;
pb_variable y;
pb_variable sym_2;
pb_variable out;

下面將各個變量與protoboard 連接,相當於把各個元器件插到“麵包板”上。 allocate() 函數的第二個string 類型變量僅是用來方便DEBUG 時的註釋,方便DEBUG 時查看日誌。


out.allocate(pb, "out");
x.allocate(pb, "x");
sym_1.allocate(pb, "sym_1");
y.allocate(pb, "y");
sym_2.allocate(pb, "sym_2");
pb.set_input_sizes(1);

注意,此處第一個與pb 連接的是out 變量。我們知道zk-SNARKs 中有public input 和private witness 的概念,分別對應libsnark 中的primary 和auxiliary 變量。那麼如何在代碼中進行區分呢?我們需要藉助set_input_sizes(n) 來聲明與protoboard 連接的public/primary 變量的個數n。在這裡n = 1,表明與pb 連接的前n = 1 個變量是public 的,其餘都是private 的。

至此, 所有變量都已經順利與protoboard 相連,下面需要確定的是這些變量間的約束關係。這個也很好理解,類似元器件插至麵包板後,需要根據電路需求確定他們之間的關係再連線焊接。如下調用protoboard 的add_r1cs_constraint() 函數,為pb 添加形如a * b = c 的r1cs_constraint。即r1cs_constraint(a, b,c) 中參數應該滿足a * b = c。根據註釋不難理解每個等式和約束之間的關係。

// x*x = sym_1
pb.add_r1cs_constraint(r1cs_constraint(x, x, sym_1));
// sym_1 * x = y
pb.add_r1cs_constraint(r1cs_constraint(sym_1, x, y));
// y + x = sym_2
pb.add_r1cs_constraint(r1cs_constraint(y + x, 1, sym_2));
// sym_2 + 5 = ~out
pb.add_r1cs_constraint(r1cs_constraint(sym_2 + 5, 1, out));

至此,變量間的約束也已添加完成,針對命題的電路已構建完畢。下面進入前文提到的“四個步驟”中的第二步:使用生成算法(G)為該命題生成公共參數(pk 和vk),即trusted setup。生成出來的proving key 和verification key 分別可以通過keypair.pk 和keypair.vk 獲得。

const r1cs_constraint_system constraint_system = pb.get_constraint_system();
const r1cs_gg_ppzksnark_keypair keypair = r1cs_gg_ppzksnark_generator(constraint_system);

進入第三步,生成證明。先為public input 以及witness 提供具體數值。不難發現,x = 3, out = 35 是原始方程的一個解。則依次為x、out 以及各個中間變量賦值。


pb.val(out) = 35;

pb.val(x) = 3;
pb.val(sym_1) = 9;
pb.val(y) = 27;
pb.val(sym_2) = 30;

再把public input 以及witness 的數值傳給prover 函數進行證明,可分別通過pb.primary_input() 和pb.auxiliary_input() 訪問。生成的證明用proof 變量保存。


const r1cs_gg_ppzksnark_proof proof = r1cs_gg_ppzksnark_prover(keypair.pk, pb.primary_input(), pb.auxiliary_input());

最後我們使用verifier 函數校驗證明。如果verified = true 則說明證明驗證成功。


bool verified = r1cs_gg_ppzksnark_verifier_strong_IC(keypair.vk, pb.primary_input(), proof);

從日誌輸出中可以看出驗證結果為true,R1CS 約束數量為4,public input 和private input 數量分別為1 和4。日誌輸出符合預期。

實際應用中,trusted setup、prove、verify 會由不同角色分別開展,最終實現的效果就是prover 給verifier 一段簡短的proof 和public input,verifier 可以自行校驗某命題是否成立。對於前面的例子,就是能在不知道方程的解x 具體是多少的情況下,驗證prover 知道一個秘密的x 可以使得x^3 + x + 5 = out 成立。

通過短短的幾十行代碼,你就可以很輕易地操控學術界zk-SNARKs 最新研究成果。

5. 再次上手實踐

經過上面的例子,我們已經了解了利用libsnark 庫開發zk-SNARKs 電路的所有重要步驟。

現在不妨用新的例子來鞏固一下:在不洩露秘密數字大小的前提下,證明數字小於60。

這個在常規程序裡用一個運算符就能完成的事情,在libsnark 下面應該如何表示呢?

zk-SNARKs 電路開發的主要工作量和難點在於如何用代碼“精確”地描述命題中的所有約束。一旦描述不“精確”,則要么是漏掉約束、要么是寫錯約束,最終電路想要證明的內容則會與原命題相差甚遠。上一節的例子只涉及簡單的乘法和加法,與r1cs_constraint 最基本的形式一致,因此約束的表達相對容易。除此之外幾乎所有的約束都不是很直觀,作為初學者很難正確地描述約束細節。

幸好libsnark 已經為我們實現了大量基礎電路小組件。 gadgetlib1 和gadgetlib2 下提供了許多可以直接使用的gadget。其中gadgetlib1 更常用一些,裡面收集了包括sha256 在內的hash 計算、merkle tree、pairing 等電路實現。

DangDangDang,gadgetlib1/gadgets/basic_gadgets.hpp 中的comparison_gadget 正是我們所需。


comparison_gadget(protoboard& pb,
                   const size_t n,
                   const pb_linear_combination &A,
                   const pb_linear_combination &B,
                   const pb_variable &less,
                   const pb_variable &less_or_eq,
                   const std::string &annotation_prefix="")

::generate_r1cs_constraints() 研究。::generate_r1cs_constraints() 研究。

這裡需要創建以下變量,並將x 和max 與pb 相連,把max 值設為60,代表數值上限。


protoboard pb;

pb_variable x, max;
pb_variable less, less_or_eq;

x.allocate(pb, "x");
max.allocate(pb, "max");

pb.val(max)= 60;

使用comparison_gadget 創建cmp,並把前面的參數填入,並調用gadget 自帶的generate_r1cs_constraints() 方法。同時另外添加一個約束,要求less * 1 = 1,也就是less 必須為true。


comparison_gadget cmp(pb, 10, x, max, less, less_or_eq, "cmp");
cmp.generate_r1cs_constraints();
pb.add_r1cs_constraint(r1cs_constraint(less, 1, FieldT::one()));

輸入witness(秘密值x),比如讓x = 18。這裡還需要調用comparison_gadget 的generate_r1cs_witness 方法。


// Add witness values
pb.val(x) = 18; // secret
cmp.generate_r1cs_witness();

其餘部分和上一個例子一致,即可在不洩露秘密數字大小的前提下,證明某數字小於60。同理,就實現一個對數值作最大和最小值限定的“range proof”。

在強大基礎庫的幫助下,我們又用更短的代碼實現了證明需求。

6. What's NEXT?

讀到這裡,相信大家都對libsnark 的使用方法和zk-SNARKs 電路開發有了一個初步的了解。

你或許已經發現,libsnark 的使用方法較簡單,而真正的重點在於zk-SNARKs 電路開發。正如前面提過的,必須用代碼“精確”描述待證命題中的所有約束,“漏掉”或“寫錯”約束都會讓證明內容與原本意圖大相徑庭,從而導致證明無意義。

如何正確高效地把真實業務邏輯轉化為zk-SNARKs 電路代碼,這正是我們開發者需要不斷研究和練習的。

好在我們已經有了一個libsnark 試驗場,可以很方便地自由修改、添加代碼來嘗試。

不論多複雜的電路實現,都是通過一個個更簡單地「電路組件」組合封裝而形成。因此libsnark 自帶的基礎庫是一個非常重要的學習資料——既要學習它們的使用方法,又要研究其實現原理。

我們也能通過閱讀其他項目的電路實現來了解如何將ZKP 應用到實際業務中,如HarryR 的ethsnarks-miximus和Loopring 的protocol3-circuits。從這些項目中可以學習到如何工程化地開發更大規模的電路,以及與電路性能相關的各種設計優化細節,同時對電路約束規模會有更深刻的理解。

同時也歡迎大家繼續關注安比實驗室「零知識證明Learn by Coding:libsnark 系列」後續文章,下次我們將嘗試從zk-SNARKs 與智能合約的結合、電路模塊化開發、更複雜的libsnark 實現案例、電路開發過程中容易踩的坑等角度來進一步討論。

7. 附錄

main.cpp

第一個例子main.cpp,調用libsnark 官方example 的示例代碼。通過該例子可了解libsnark 的基本使用流程和主要函數。


#include
#include
#include
#include

using namespace libsnark;

/**
* The code below provides an example of all stages of running a R1CS GG-ppzkSNARK.
*
* Of course, in a real-life scenario, we would have three distinct entities,
* mangled into one in the demonstration below. The three entities are as follows.
* (1) The "generator", which runs the ppzkSNARK generator on input a given
*     constraint system CS to create a proving and a verification key for CS.
* (2) The "prover", which runs the ppzkSNARK prover on input the proving key,
*     a primary input for CS, and an auxiliary input for CS.
* (3) The "verifier", which runs the ppzkSNARK verifier on input the verification key,
*     a primary input for CS, and a proof.
*/
template
bool run_r1cs_gg_ppzksnark(const r1cs_example > &example)
{
   libff::print_header("R1CS GG-ppzkSNARK Generator");
   r1cs_gg_ppzksnark_keypair keypair = r1cs_gg_ppzksnark_generator(example.constraint_system);
   printf("\n"); libff::print_indent(); libff::print_mem("after generator");

   libff::print_header("Preprocess verification key");
   r1cs_gg_ppzksnark_processed_verification_key pvk = r1cs_gg_ppzksnark_verifier_process_vk(keypair.vk);

   libff::print_header("R1CS GG-ppzkSNARK Prover");
   r1cs_gg_ppzksnark_proof proof = r1cs_gg_ppzksnark_prover(keypair.pk, example.primary_input, example.auxiliary_input);
   printf("\n"); libff::print_indent(); libff::print_mem("after prover");

   libff::print_header("R1CS GG-ppzkSNARK Verifier");
   const bool ans = r1cs_gg_ppzksnark_verifier_strong_IC(keypair.vk, example.primary_input, proof);
   printf("\n"); libff::print_indent(); libff::print_mem("after verifier");
   printf("* The verification result is: %s\n", (ans ? "PASS" : "FAIL"));

   libff::print_header("R1CS GG-ppzkSNARK Online Verifier");
   const bool ans2 = r1cs_gg_ppzksnark_online_verifier_strong_IC(pvk, example.primary_input, proof);
   assert(ans == ans2);

   return ans;
}

template
void test_r1cs_gg_ppzksnark(size_t num_constraints, size_t input_size)
{
   r1cs_example > example = generate_r1cs_example_with_binary_input >(num_constraints, input_size);
   const bool bit = run_r1cs_gg_ppzksnark(example);
   assert(bit);
}

int main () {
   default_r1cs_gg_ppzksnark_pp::init_public_params();
   test_r1cs_gg_ppzksnark(1000, 100);

   return 0;
}

test.cpp

第二個例子test.cpp。這個例子具體展示瞭如何利用libsnark 構建一個最簡單的電路。


#include
#include
#include

using namespace libsnark;
using namespace std;

int main () {
   typedef libff::Fr FieldT;

   // Initialize the curve parameters
   default_r1cs_gg_ppzksnark_pp::init_public_params();
 
   // Create protoboard
   protoboard pb;

   // Define variables
   pb_variable x;
   pb_variable sym_1;
   pb_variable y;
   pb_variable sym_2;
   pb_variable out;

   // Allocate variables to protoboard
   // The strings (like "x") are only for debugging purposes    
   out.allocate(pb, "out");
   x.allocate(pb, "x");
   sym_1.allocate(pb, "sym_1");
   y.allocate(pb, "y");
   sym_2.allocate(pb, "sym_2");

   // This sets up the protoboard variables
   // so that the first one (out) represents the public
   // input and the rest is private input
   pb.set_input_sizes(1);

   // Add R1CS constraints to protoboard

   // x*x = sym_1
   pb.add_r1cs_constraint(r1cs_constraint(x, x, sym_1));

   // sym_1 * x = y
   pb.add_r1cs_constraint(r1cs_constraint(sym_1, x, y));

   // y + x = sym_2
   pb.add_r1cs_constraint(r1cs_constraint(y + x, 1, sym_2));

   // sym_2 + 5 = ~out
   pb.add_r1cs_constraint(r1cs_constraint(sym_2 + 5, 1, out));
   
   const r1cs_constraint_system constraint_system = pb.get_constraint_system();

   // generate keypair
   const r1cs_gg_ppzksnark_keypair keypair = r1cs_gg_ppzksnark_generator(constraint_system);

   // Add public input and witness values
   pb.val(out) = 35;

   pb.val(x) = 3;
   pb.val(sym_1) = 9;
   pb.val(y) = 27;
   pb.val(sym_2) = 30;

   // generate proof
   const r1cs_gg_ppzksnark_proof proof = r1cs_gg_ppzksnark_prover(keypair.pk, pb.primary_input(), pb.auxiliary_input());

   // verify
   bool verified = r1cs_gg_ppzksnark_verifier_strong_IC(keypair.vk, pb.primary_input(), proof);

   cout << "Number of R1CS constraints: " << constraint_system.num_constraints() << endl;
   cout << "Primary (public) input: " << pb.primary_input() << endl;
   cout << "Auxiliary (private) input: " << pb.auxiliary_input() << endl;
   cout << "Verification status: " << verified << endl;
}

range.cpp

第三個例子range.cpp。該例子利用了libsnark 自帶的comparison_gadget 來實現取值範圍證明。

#include

#include

#include

#include


using namespace libsnark;

using namespace std;


int main () {

    typedef libff::Fr FieldT;


    // Initialize the curve parameters

    default_r1cs_gg_ppzksnark_pp::init_public_params();

  

    // Create protoboard

    protoboard pb;


    pb_variable x, max;

    pb_variable less, less_or_eq;


    x.allocate(pb, "x");

    max.allocate(pb, "max");

    

    pb.val(max)= 60;


    comparison_gadget cmp(pb, 10, x, max, less, less_or_eq, "cmp");

    cmp.generate_r1cs_constraints();

    pb.add_r1cs_constraint(r1cs_constraint(less, 1, FieldT::one()));


    const r1cs_constraint_system constraint_system = pb.get_constraint_system();


    // generate keypair

    const r1cs_gg_ppzksnark_keypair keypair = r1cs_gg_ppzksnark_generator(constraint_system);


    // Add witness values

    pb.val(x) = 18; // secret

    cmp.generate_r1cs_witness();


    // generate proof

    const r1cs_gg_ppzksnark_proof proof = r1cs_gg_ppzksnark_prover(keypair.pk, pb.primary_input(), pb.auxiliary_input());


    // verify

    bool verified = r1cs_gg_ppzksnark_verifier_strong_IC(keypair.vk, pb.primary_input(), proof);


    cout << "Number of R1CS constraints: " << constraint_system.num_constraints() << endl;

    cout << "Primary (public) input: " << pb.primary_input() << endl;

    cout << "Auxiliary (private) input: " << pb.auxiliary_input() << endl;

    cout << "Verification status: " << verified << endl;

}


安比(SECBIT)实验室
作者文库