原文出自:libsnark庫
作者:libsnark貢獻(xiàn)者
譯者:Matter實(shí)驗(yàn)室
gadgetlib2庫的使用以及如何整合ppzkSNARK的教程和范例。這分文檔專為小零件(gadget)寫的,推薦從上至下作為教程來閱讀。
1. 面包板(protoboard)的使用和范例
這個(gè)測(cè)試給出構(gòu)造系統(tǒng)限制(constraint)的第一個(gè)范例。我們將時(shí)不時(shí)交替使用 ‘系統(tǒng)限制’(Constraint) 和 ‘電路’(Circuit) 兩個(gè)術(shù)語. 用輸入和輸出形象化一個(gè)電路非常容易。每個(gè)門強(qiáng)制規(guī)定輸入和輸出線之間的邏輯。例如,AND(inp1,inp2) 會(huì)強(qiáng)制 (inp1 & inp2 = out) 這個(gè)限制。因此,我們也能認(rèn)為這個(gè)電路是一個(gè)限制系統(tǒng)。每個(gè)門是一個(gè)數(shù)學(xué)限制,每個(gè)線是一個(gè)變量。在AND的例子中,通過boolean類型{0,1}我們將寫出一個(gè)像(inp1*inp2==out)這樣的限制。如果我們指派值到{inp1,inp2,out},并且這個(gè)限制對(duì)于這樣的賦值是滿足的,因此對(duì)這個(gè)限制的評(píng)估就是TRUE。所有接下來的例子是要么是領(lǐng)域不可知或素?cái)?shù)字段的特殊形式。
領(lǐng)域不可知情況:在這些例子中,我們用低級(jí)電路來創(chuàng)建一個(gè)高級(jí)電路。這種方法我們可以忽略一個(gè)字段的具體的情況,并假定低級(jí)電路會(huì)處理這個(gè)。如果我們必須在這些電路中顯示的寫出限制。這種情況常常是一下非?;A(chǔ)的限制,并可以定義在每個(gè)字段上,例如(e.g. x+y=0)。
所有字段都具體的例子,在這個(gè)庫中,是對(duì)素?cái)?shù)性質(zhì)的字段來說的,采用的是‘二次秩1多項(xiàng)式’或者叫R1P 。這是當(dāng)前SNARKs實(shí)現(xiàn)的唯一形式。這種形式專門處理像(Linear Combination) * (Linear Combination) == (Linear Combination)這樣的限制。這個(gè)庫已經(jīng)被設(shè)計(jì)成允許未來加入其他的特性或形式,目前只為這樣的形式實(shí)現(xiàn)了低級(jí)電路。
1. 代碼與過程
初始化素?cái)?shù)字段參數(shù),R1P總是需要這么做。
initPublicParamsFromDefaultPp();
面包版是一個(gè)“內(nèi)存管理器”,它維護(hù)所有的限制(當(dāng)創(chuàng)建驗(yàn)證電路的時(shí)候)和變量賦值(當(dāng)創(chuàng)建證明證人的時(shí)候)。我們特別說明這個(gè)R1P的類型,能在未來增加對(duì)BOOLEAN或GF2_EXTENSION字段的支持。
ProtoboardPtr pb = Protoboard::create(R1P);
現(xiàn)在我們創(chuàng)建三個(gè)輸入和一個(gè)輸出
VariableArray input(3, "input");
Variable output("output");
我們現(xiàn)在可以添加一些限制,其中字符串的目的是為了調(diào)試方便,并且能夠給這個(gè)限制一個(gè)文本說明。
pb->addRank1Constraint(
input[0],
5 + input[2],
output,
"Constraint 1: input[0] * (5 + input[2]) == output"
);
第二種形式是添加一個(gè)一元限制,這個(gè)意味著(LinearCombination == 0)
pb->addUnaryConstraint(
input[1] - output,
"Constraint 2: input[1] - output == 0"
);
注意到這樣寫也是可以的:
pb->addRank1Constraint(1, input[1] - input[2], 0, "");
對(duì)于更加一般化形式的字段,可以一次性實(shí)現(xiàn),我們可以使用addGeneralConstraint(Polynomial1, Polynomial2, string)會(huì)轉(zhuǎn)換成(Polynomial1 == Polynomial2)限制。例如:
pb->addGeneralConstraint(
input[0] * (3 + input[1]) * input[2],
output + 5,
"input[0] * (3 + input[1]) * input[2] == output + 5"
);
現(xiàn)在我們能將值賦給變量了,并且看一看限制是否滿足。之后,當(dāng)我們運(yùn)行SNARK(或者任何其他證明系統(tǒng))的時(shí)候,這些限制將會(huì)被驗(yàn)證者(verifier)使用,并且指派的值被證明者(prover)使用。
注意到面包版(protoboard)存儲(chǔ)了被指派的值。
pb->val(input[0]) =
pb->val(input[1]) =
pb->val(input[2]) =
pb->val(output) = 42;
EXPECT_FALSE(pb->isSatisfied());
這個(gè)限制系統(tǒng)是沒有被滿足的,現(xiàn)在讓我們嘗試一下可以滿足上面兩個(gè)等式的值。
The constraint system is not satisfied. Now let's try values which satisfy the two equations above:
pb->val(input[0]) = 1;
pb->val(input[1]) =
pb->val(output) = 42; // input[1]-output == 0
pb->val(input[2]) = 37; // 1*(5+37)==42
EXPECT_TRUE(pb->isSatisfied());
2. Gadget與非的例子
在上面的例子中,我們清晰的寫了所有的限制和賦值。
在這個(gè)例子中,我們將構(gòu)造一個(gè)非常簡(jiǎn)單的gadget,一個(gè)實(shí)現(xiàn)了與非(NAND)的門。這個(gè)gadget是字段無關(guān)的,因?yàn)樗皇褂昧说图?jí)的gadget以及字段元素 '0' & '1'。
Gadget是一個(gè)允許我們委托復(fù)雜電路元件到低層的框架。依靠限定與賦值以及利用子gadget,每一個(gè)gadget能夠構(gòu)造一個(gè)限定系統(tǒng)或證明者,或是兩者一起。。
1. 主過程
下面這個(gè)測(cè)試用來說明使用方法:
初始化字段
initPublicParamsFromDefaultPp();
用R1P系統(tǒng)的類型創(chuàng)建一個(gè)面包板。
ProtoboardPtr pb = Protoboard::create(R1P);
創(chuàng)建5個(gè)變量inputs[0]...inputs[4]。字符串“inputs”是調(diào)試信息。
FlagVariableArray inputs(5, "inputs");
FlagVariable output("output");
GadgetPtr nandGadget = NAND_Gadget::create(pb, inputs, output);
現(xiàn)在我們能生成一個(gè)限定系統(tǒng)(或電路)
nandGadget->generateConstraints();
如果我們現(xiàn)在嘗試去計(jì)算這個(gè)電路,一個(gè)異常會(huì)被拋出來,因?yàn)槲覀兤髨D計(jì)算沒有賦值的變量。
EXPECT_ANY_THROW(pb->isSatisfied());
因此讓我們對(duì)這個(gè)輸入變量賦值,這么就可以進(jìn)行與非(NAND)計(jì)算,并且在創(chuàng)建證據(jù)之后,再次嘗試計(jì)算這個(gè)電路。
for (const auto& input : inputs) {
pb->val(input) = 1;
}
nandGadget->generateWitness();
EXPECT_TRUE(pb->isSatisfied());
EXPECT_TRUE(pb->val(output) == 0);
現(xiàn)在讓我們毀壞某些東西,并看看發(fā)生了什么?
pb->val(inputs[2]) = 0;
EXPECT_FALSE(pb->isSatisfied());
現(xiàn)在讓我們嘗試欺騙一下。如果我們沒有強(qiáng)制要求boolean化,這應(yīng)該是能運(yùn)行的。
pb->val(inputs[1]) = 2;
EXPECT_FALSE(pb->isSatisfied());
現(xiàn)在讓我們重新設(shè)置inputs[1]為一個(gè)正確的值。
pb->val(inputs[1]) = 1;
之前我們?cè)O(shè)置了inputs和output。注意到output仍然是'0'。
// before, we set both the inputs and the output. Notice the output is still set to '0'
EXPECT_TRUE(pb->val(output) == 0);
現(xiàn)在我們將使gadget利用generateWitness() 創(chuàng)建證據(jù)計(jì)算出結(jié)果并且看發(fā)生了什么?
nandGadget->generateWitness();
EXPECT_TRUE(pb->val(output) == 1);
EXPECT_TRUE(pb->isSatisfied());
3. hash難度執(zhí)行者例子
另外一個(gè)例子展示雙重變量的使用。一個(gè)雙重變量是一個(gè)具有下面兩種特性的變量,字的安位表現(xiàn)形式,以及可包裝的展現(xiàn)形式(例如,包裝值 {42} 和非包裝值 {1,0,1,0,1,0} )。如果這個(gè)字夠短(比如,小于它主要特征的任何整數(shù)),那么包裝起來的表達(dá)方式將被存儲(chǔ)在一個(gè)元素字段中。字在這個(gè)上下文中的意思是一組二進(jìn)制位,這是一個(gè)慣例,意味著我們期待一些語義能力去分解包裝的值到二進(jìn)制位去。
使用雙重變量是為了提高效率。更多的展示在例子的結(jié)尾。在這個(gè)例子中我們將構(gòu)造一個(gè)gadget,這個(gè)gadget接收一個(gè)包裝的整數(shù)作為輸入稱為'hash',和一個(gè)二進(jìn)制位的‘難度’級(jí)別,以及構(gòu)造一個(gè)用來證明‘hash’上第一個(gè)‘難度’位是0的電路。為了簡(jiǎn)單,我們將假定‘hash’總是64位長度。
1. 主過程
記得我們指出,雙重變量被用來提升效率?,F(xiàn)在就是詳細(xì)說明這個(gè)的時(shí)候。就像你看到的,我們需要一個(gè)位表達(dá)來檢查hashValue的第一個(gè)位。但是hashValue可能被用于很多其他的地方,因?yàn)槲覀兿胍獧z查實(shí)例是否與另一個(gè)值相等。在包裝的表達(dá)上檢查相等會(huì)‘消耗’我們一個(gè)限定,當(dāng)在沒有包裝的值上檢查相等的時(shí)候,將‘消耗’我們64個(gè)限制。在ppzkSNARK證明系統(tǒng)中,這個(gè)轉(zhuǎn)換會(huì)加重構(gòu)造證明的時(shí)間和內(nèi)存消耗。
initPublicParamsFromDefaultPp();
auto pb = Protoboard::create(R1P);
const MultiPackedWord hashValue(64, R1P, "hashValue");
const size_t difficulty = 10;
auto difficultyEnforcer = HashDifficultyEnforcer_Gadget::create(pb, hashValue, difficulty);
difficultyEnforcer->generateConstraints();
現(xiàn)在限制以及創(chuàng)建,但是還沒有賦值。在計(jì)算是會(huì)拋出異常。
EXPECT_ANY_THROW(pb->isSatisfied());
pb->val(hashValue[0]) = 42;
difficultyEnforcer->generateWitness();
42的起始的10個(gè)位(當(dāng)以64位數(shù)形式展示的時(shí)候)都是'0',因此應(yīng)該可以工作。
EXPECT_TRUE(pb->isSatisfied(PrintOptions::DBG_PRINT_IF_NOT_SATISFIED));
pb->val(hashValue[0]) = 1000000000000000000;
這個(gè)值大于2^54,因此我們期待限制系統(tǒng)不會(huì)被滿足。
在斷言之前,generateWitness應(yīng)該就失敗了。
difficultyEnforcer->generateWitness();
EXPECT_FALSE(pb->isSatisfied());
4. R1P驗(yàn)證交易金額例子
在這個(gè)例子中,我們將構(gòu)造一個(gè)gadget,這個(gè)gadget構(gòu)建了一個(gè)證明(證據(jù))電路,并且認(rèn)可(限制)一個(gè)比特幣交易輸入的數(shù)量必須等于輸出+礦工費(fèi)。證明的限制將包括找到礦工的費(fèi)用。這個(gè)費(fèi)用能夠作為電路的輸出被考慮。
這是特定領(lǐng)域的gadget,就像我們將要用的 '+'操作一樣自由。在主特性領(lǐng)域而非在擴(kuò)展領(lǐng)域,加法操作像所期望的一樣工作在整數(shù)上。如果你不熟悉擴(kuò)展領(lǐng)域,不用擔(dān)心。應(yīng)該簡(jiǎn)單的意識(shí)到 + 和 * 在不同的領(lǐng)域表現(xiàn)得不一樣,并且不一定給出你期望的整數(shù)值。
由于要用到不同的場(chǎng)景下,這個(gè)庫的設(shè)計(jì)支持多域構(gòu)造。當(dāng)其他的應(yīng)用使用主要域的時(shí)候一些加密圖應(yīng)用可能需要擴(kuò)展域,但是,用使用非rank-1的限制,并且有些可能還需要boolean電路。在新域或限制構(gòu)造上,這個(gè)庫的設(shè)計(jì)使高層次的gadget能夠重用底層的實(shí)現(xiàn)。
之后通過使用不可知接口,我們將提供一個(gè)創(chuàng)建這種特殊領(lǐng)域gadget的訣竅。我們?cè)谶@兒使用一些慣例,采用宏將這個(gè)過程變得容易。
1. Gadget定義
這是一個(gè)為所有的特定域創(chuàng)建一個(gè)繼承自gadget接口類的宏。
慣用法是:class {GadgetName}_GadgetBase
CREATE_GADGET_BASE_CLASS(VerifyTransactionAmounts_GadgetBase);
注意這里的多繼承。我們必須指定接口以及基礎(chǔ)gadget的特殊域。這允許類工廠在編譯期決定,特定的類需要為每個(gè)面包板實(shí)例化哪種域。可以在"gadget.hpp"中看到這個(gè)設(shè)計(jì)的信息。
慣用法:class {FieldType}_{GadgetName}_Gadget
#1: 我們給出工廠類的友元訪問為了實(shí)例化私有的構(gòu)造函數(shù)。
class R1P_VerifyTransactionAmounts_Gadget : public VerifyTransactionAmounts_GadgetBase,
public R1P_Gadget {
public:
void generateConstraints();
void generateWitness();
friend class VerifyTransactionAmounts_Gadget; // #1
private:
R1P_VerifyTransactionAmounts_Gadget(ProtoboardPtr pb,
const VariableArray& txInputAmounts,
const VariableArray& txOutputAmounts,
const Variable& minersFee);
void init();
const VariableArray txInputAmounts_;
const VariableArray txOutputAmounts_;
const Variable minersFee_;
DISALLOW_COPY_AND_ASSIGN(R1P_VerifyTransactionAmounts_Gadget);
};
使用宏CREATE_GADGET_FACTORY_CLASS_XX創(chuàng)建工廠類(用構(gòu)造函數(shù)的參數(shù)數(shù)量替換XX,protoboard不算)。有時(shí)候我們像要多構(gòu)造函數(shù),可以參照AND_Gadget。在這個(gè)場(chǎng)景下,我們將手工寫出工廠類。
CREATE_GADGET_FACTORY_CLASS_3(
VerifyTransactionAmounts_Gadget,
VariableArray, txInputAmounts,
VariableArray, txOutputAmounts,
Variable, minersFee
);
2. Gadget實(shí)現(xiàn)
為基類實(shí)現(xiàn)空析構(gòu)函數(shù)
VerifyTransactionAmounts_GadgetBase::~VerifyTransactionAmounts_GadgetBase() {}
看起來下面這點(diǎn)實(shí)現(xiàn)就足夠了,但是一個(gè)對(duì)抗者可能在域的模數(shù)上引起等式的一邊溢出。事實(shí)上,對(duì)于每個(gè)輸入/輸出的和我們總是會(huì)找到一個(gè)礦工的服務(wù)費(fèi)滿足這個(gè)限制。只剩下一個(gè)給讀者的練習(xí),實(shí)現(xiàn)一個(gè)加法限制(和證據(jù)),用來檢查每個(gè)數(shù)量(輸入,輸出,費(fèi)用)在0和21,000,000 * 1E8 中本村之間。用最大的輸入/輸出數(shù)量組合這個(gè)數(shù)防止域溢出。
提示:使用Comparison_Gadget創(chuàng)建gadget,這個(gè)gadget將一個(gè)變量的值與常數(shù)做比較。使用這些新gadget的數(shù)組去檢查各自的值。
不要忘記:
- 在init()中連接這些gadgets。
- 在generateConstraints()中調(diào)用這些gadgets的限定。
- 在generateWitness()中調(diào)用這些gadgets的證據(jù)。
#1 注意我們必須初始化3個(gè)基類(菱形繼承)
void R1P_VerifyTransactionAmounts_Gadget::generateConstraints() {
addUnaryConstraint(
sum(txInputAmounts_) - sum(txOutputAmounts_) - minersFee_,
"sum(txInputAmounts) == sum(txOutputAmounts) + minersFee"
);
}
void R1P_VerifyTransactionAmounts_Gadget::generateWitness() {
FElem sumInputs = 0;
FElem sumOutputs = 0;
for (const auto& inputAmount : txInputAmounts_) {
sumInputs += val(inputAmount);
}
for (const auto& outputAmount : txOutputAmounts_) {
sumOutputs += val(outputAmount);
}
val(minersFee_) = sumInputs - sumOutputs;
}
R1P_VerifyTransactionAmounts_Gadget::R1P_VerifyTransactionAmounts_Gadget(
ProtoboardPtr pb,
const VariableArray& txInputAmounts,
const VariableArray& txOutputAmounts,
const Variable& minersFee
):
Gadget(pb),
VerifyTransactionAmounts_GadgetBase(pb),
R1P_Gadget(pb), // #1
txInputAmounts_(txInputAmounts),
txOutputAmounts_(txOutputAmounts),
minersFee_(minersFee) {}
void R1P_VerifyTransactionAmounts_Gadget::init() {}
2. 主流程
按照約定,用不可知接口創(chuàng)建一個(gè)特定域gadgets的訣竅是:
用宏創(chuàng)建一個(gè)基類:
CREATE_GADGET_BASE_CLASS({GadgetName}_GadgetBase);為基類創(chuàng)建一個(gè)構(gòu)造函數(shù):
{GadgetName_Gadget}Base::~{GadgetName}_GadgetBase() {}-
使用多繼承創(chuàng)建你需要的特定域gadgets。
class {FieldType}_{GadgetName}_Gadget :
public {GadgetName}_GadgetBase,
public {FieldType_Gadget}注意到為了使用工廠類宏,構(gòu)造函數(shù)的所有參數(shù)必須是 const&。構(gòu)造參數(shù)必須與所有的特定域?qū)崿F(xiàn)一致。
給工廠類{GadgetName}_Gadget友元訪問特定域類的權(quán)限。
采用宏創(chuàng)建工廠類。
CREATE_GADGET_FACTORY_CLASS_XX(
{GadgetName}_Gadget,
type1, input1, type2, input2, ... ,
typeXX, inputXX
);
void examples_r1p_verify_transaction_amounts() {
initPublicParamsFromDefaultPp();
auto pb = Protoboard::create(R1P);
const VariableArray inputAmounts(2, "inputAmounts");
const VariableArray outputAmounts(3, "outputAmounts");
const Variable minersFee("minersFee");
auto verifyTx = VerifyTransactionAmounts_Gadget::create(
pb,
inputAmounts,
outputAmounts,
minersFee
);
verifyTx->generateConstraints();
pb->val(inputAmounts[0]) = pb->val(inputAmounts[1]) = 2;
pb->val(outputAmounts[0]) =
pb->val(outputAmounts[1]) =
pb->val(outputAmounts[2]) = 1;
verifyTx->generateWitness();
EXPECT_TRUE(pb->isSatisfied());
EXPECT_EQ(pb->val(minersFee), 1);
pb->val(minersFee) = 3;
EXPECT_FALSE(pb->isSatisfied());
}
5. 整合gadgetlib2和ppzkSNARK
下面是一個(gè)整合gadgetlib2所構(gòu)造的限制系統(tǒng)和ppzkSNARK的例子。
1. 主流程
initPublicParamsFromDefaultPp();
創(chuàng)建一個(gè)限制系統(tǒng)的例子,并翻譯成libsnark的格式化。
const libsnark::r1cs_example<
libff::Fr<
libff::default_ec_pp
>
> example = libsnark::gen_r1cs_example_from_gadgetlib2_protoboard(100);
const bool test_serialization = false;
運(yùn)行ppzksnark,跳轉(zhuǎn)到分析函數(shù)。
const bool bit = libsnark::run_r1cs_ppzksnark<
libff::default_ec_pp
>(example, test_serialization);
EXPECT_TRUE(bit);
2. r1cs創(chuàng)建
限制系統(tǒng)在gen_r1cs_example_from_gadgetlib2_protoboard中創(chuàng)建,我們來看看里面的過程是什么。
注意:這個(gè)例子確實(shí)創(chuàng)建了一個(gè)限制,這個(gè)限制至少說明說明了QAP限制的健全性。
r1cs_example<
libff::Fr<
libff::default_ec_pp
>
>
gen_r1cs_example_from_gadgetlib2_protoboard(const size_t size)
{
typedef libff::Fr<libff::default_ec_pp> FieldT;
gadgetlib2::initPublicParamsFromDefaultPp();
必要的情況下,在之前就建立一個(gè)面包板,libsnark假定變量索引總是從0開始,因此我們必須在創(chuàng)建被libsnark使用的限制之前重設(shè)索引。
gadgetlib2::GadgetLibAdapter::resetVariableIndex();
創(chuàng)建一個(gè)gadgetlib2的gadget。這個(gè)部分靠生成器和證明器實(shí)現(xiàn)。
auto pb = gadgetlib2::Protoboard::create(gadgetlib2::R1P);
gadgetlib2::VariableArray A(size, "A");
gadgetlib2::VariableArray B(size, "B");
gadgetlib2::Variable result("result");
auto g = gadgetlib2::InnerProduct_Gadget::create(pb, A, B, result);
創(chuàng)建一個(gè)限制,這個(gè)部分靠生成器完成。
g->generateConstraints();
創(chuàng)建賦值(證據(jù))。這個(gè)部分是證明者完成的。
for (size_t k = 0; k < size; ++k)
{
pb->val(A[k]) = std::rand() % 2;
pb->val(B[k]) = std::rand() % 2;
}
g->generateWitness();
將限制系統(tǒng)轉(zhuǎn)換成libsnark格式。
r1cs_constraint_system<FieldT> cs =
get_constraint_system_from_gadgetlib2(*pb);
轉(zhuǎn)換所有的變量的賦值到libsnark格式。
const r1cs_variable_assignment<FieldT> full_assignment =
get_variable_assignment_from_gadgetlib2(*pb);
提取主要的和輔助的輸入
const r1cs_primary_input<FieldT> primary_input(
full_assignment.begin(),
full_assignment.begin() + cs.num_inputs()
);
const r1cs_auxiliary_input<FieldT> auxiliary_input(
full_assignment.begin() + cs.num_inputs(),
full_assignment.end()
);
assert(cs.is_valid());
assert(cs.is_satisfied(primary_input, auxiliary_input));
return r1cs_example<FieldT>(cs, primary_input, auxiliary_input);
}
3. 運(yùn)行ppzksnark
下面的代碼提供了一個(gè)以R1CS的形式運(yùn)行ppzkSNARK的全場(chǎng)景的例子。
當(dāng)然,在實(shí)際的情景中,我們有三個(gè)明顯的實(shí)體,下面將亂入到一個(gè)演示實(shí)例中。這三種實(shí)體如下:
生成器:它運(yùn)行ppzkSNARK生成器,輸入一個(gè)給定的限制系統(tǒng)CS來創(chuàng)建一個(gè)證明過程和一個(gè)對(duì)于CS來說可驗(yàn)證的key。
證明者:運(yùn)行ppzkSNARK的證明者,輸入一個(gè)證明用的key。
驗(yàn)證者:運(yùn)行ppzkSNARK驗(yàn)證者,輸入一個(gè)可驗(yàn)證的key,一個(gè)CS的主輸入,和一個(gè)證據(jù)。
template<typename ppT>
bool run_r1cs_ppzksnark(const r1cs_example<libff::Fr<ppT> > &example,
const bool test_serialization)
{
//libff::enter_block("Call to run_r1cs_ppzksnark");
//libff::print_header("R1CS ppzkSNARK Generator");
r1cs_ppzksnark_keypair<ppT> keypair =
r1cs_ppzksnark_generator<ppT>(example.constraint_system);
//printf("\n");
//libff::print_indent();
//libff::print_mem("after generator");
//libff::print_header("Preprocess verification key");
r1cs_ppzksnark_processed_verification_key<ppT> pvk =
r1cs_ppzksnark_verifier_process_vk<ppT>(keypair.vk);
if (test_serialization) {
//libff::enter_block("Test serialization of keys");
keypair.pk =
libff::reserialize<
r1cs_ppzksnark_proving_key<ppT>
>(keypair.pk);
keypair.vk =
libff::reserialize<
r1cs_ppzksnark_verification_key<ppT>
>(keypair.vk);
pvk =
libff::reserialize<
r1cs_ppzksnark_processed_verification_key<ppT>
>(pvk);
//libff::leave_block("Test serialization of keys");
}
//libff::print_header("R1CS ppzkSNARK Prover");
r1cs_ppzksnark_proof<ppT> proof =
r1cs_ppzksnark_prover<ppT>(
keypair.pk,
example.primary_input,
example.auxiliary_input
);
//printf("\n");
//libff::print_indent();
//libff::print_mem("after prover");
if (test_serialization)
{
//libff::enter_block("Test serialization of proof");
proof = libff::reserialize<
r1cs_ppzksnark_proof<ppT>
>(proof);
//libff::leave_block("Test serialization of proof");
}
//libff::print_header("R1CS ppzkSNARK Verifier");
const bool ans =
r1cs_ppzksnark_verifier_strong_IC<ppT>(
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 ppzkSNARK Online Verifier");
const bool ans2 =
r1cs_ppzksnark_online_verifier_strong_IC<ppT>(
pvk,
example.primary_input,
proof
);
assert(ans == ans2);
test_affine_verifier<ppT>(
keypair.vk,
example.primary_input,
proof,
ans
);
//libff::leave_block("Call to run_r1cs_ppzksnark");
return ans;
}
譯者總結(jié):
