इस अध्याय में आवश्यक syntax को कवर किया गया है, जिसे आप अधिकांश Circom प्रोग्राम्स में देखेंगे। Circom के साथ, हम प्रत्येक constraint को स्पष्ट रूप से परिभाषित करने के बजाय कोड का उपयोग करके Rank 1 Constraint System (R1CS) को परिभाषित करने में सक्षम हैं। हम इस अध्याय में उन टूल्स का पता लगाएंगे।
Template Parameters
इससे पहले, हमने एक सर्किट (IsBinary) देखा था जो यह सत्यापित करता था कि दिए गए inputs वास्तव में बाइनरी थे या नहीं। उस सर्किट को केवल 2 inputs स्वीकार करने के लिए hardcode किया गया था।
template IsBinary() {
signal input in[2];
in[0] * (in[0] - 1) === 0;
in[1] * (in[1] - 1) === 0;
}
component main = IsBinary();
हालांकि उपरोक्त कोड दो inputs के लिए काम करता है, लेकिन इसे बड़े n inputs का समर्थन करने के लिए संशोधित करने पर मैन्युअल रूप से constraints जोड़ने की आवश्यकता होगी, जो एक खराब डेवलपर अनुभव (developer experience) है।
इसलिए, Circom हमें constraints को स्वचालित रूप से उत्पन्न करने के लिए निम्नलिखित पैटर्न का उपयोग करके अनियंत्रित संख्या (arbitrary number) में signals को constrain करने की अनुमति देता है:
template IsBinary(n) {
// array of n inputs
signal input in[n];
// n loops: n constraints
for (var i = 0; i < n; i++) {
in[i] * (in[i] - 1) === 0;
}
}
// instantiated w/ 4 inputs & 4 constraints
component main = IsBinary(4);
ध्यान दें कि template declaration बदल गया है और कोष्ठक (parenthesis) में n शामिल हो गया है।
- यहां
nको एक template parameter के रूप में जाना जाता है - सर्किट के भीतर
nका उपयोगinarray के आकार को निर्दिष्ट करने के लिए किया जाता है - template को instantiate करते समय, हमें
nका मान निर्दिष्ट करना होगा
Circom में सर्किट्स और constraints की एक निश्चित (fixed), ज्ञात संरचना होनी चाहिए
हालांकि constraints को प्रोग्रामेटिक रूप से उत्पन्न किया जा सकता है, constraints का अस्तित्व और कॉन्फ़िगरेशन सशर्त रूप से (conditionally) signals पर निर्भर नहीं हो सकता है।
जबकि templates parameters का उपयोग कर सकते हैं, सर्किट स्टैटिक और स्पष्ट रूप से परिभाषित होना चाहिए। “dynamic-length” सर्किट्स या constraints के लिए कोई समर्थन नहीं है — शुरुआत से ही सब कुछ निश्चित और अच्छी तरह से परिभाषित होना चाहिए।
कल्पना करें कि आपके पास constraints का एक R1CS सिस्टम है जिसकी संरचना input signal के मानों के आधार पर परिवर्तनशील (mutable) है। न तो prover और न ही verifier काम कर पाएगा क्योंकि constraints की संख्या निश्चित (set in stone) नहीं है।
n का मान compile time पर सेट किया जाना चाहिए।
For loop और Variables: for, var
अब हम ऊपर पेश किए गए for loop की व्याख्या करते हैं।
template IsBinary(n) {
// array of n inputs
signal input in[n];
// n loops: n constraints
for (var i = 0; i < n; i++) {
in[i] * (in[i] - 1) === 0;
}
}
// instantiated with 4 inputs & 4 constraints
component main = IsBinary(4);
- inputs और loop iterations दोनों
nद्वारा परिभाषित किए गए हैं - प्रत्येक input के लिए, एक constraint को यह सत्यापित करने के उद्देश्य से परिभाषित किया गया है कि input या तो
0है या1है
हमने सर्किट में दो नए keywords पेश किए हैं: for और var
forउसी तरह काम करता है जिसके आप अभ्यस्त हैं।varkeyword एक variable घोषित करता है; इस मामले में,i, जैसा कि loop की परिभाषा में देखा गया है।- बराबर का चिह्न
=दाईं ओर के मान को बाईं ओर के variable को असाइन करता है।
यहाँ, variable i का उपयोग input array में विभिन्न signals को उनके लिए constraints बनाते समय प्रोग्रामेटिक रूप से संदर्भित (refer) करने के लिए किया जाता है। प्रोग्रामेटिक रूप से constraints उत्पन्न करने में सक्षम होना अत्यधिक उपयोगी है, क्योंकि जब सैकड़ों या हजारों constraints शामिल हों तो इसे मैन्युअल रूप से करने पर त्रुटि (error) होने की संभावना बहुत अधिक होगी।
Variables
Variables नॉन-सिग्नल डेटा रखते हैं और परिवर्तनशील (mutable) होते हैं। यहाँ लूप के बाहर variable घोषणा (declaration) का एक उदाहरण दिया गया है:
template VariableExample(n) {
var acc = 2;
signal s;
}
- डिफ़ॉल्ट रूप से, variables constraints के R1CS सिस्टम का हिस्सा नहीं होते हैं।
- हम जल्द ही देखेंगे कि variables का उपयोग R1CS के अंदर एडिटिव (additive) या मल्टीप्लिकेटिव (multiplicative) स्थिरांक (constants) के रूप में किया जा सकता है।
- Variables का उपयोग R1CS को परिभाषित करने में मदद करने के लिए R1CS के बाहर के मानों की गणना करने के लिए किया जाता है।
- Variables के साथ काम करते समय, Circom एक सामान्य प्रोग्रामिंग भाषा की तरह व्यवहार करता है।
- गणितीय संचालन modulo
pपर किए जाते हैं। ऑपरेटरों की पूरी सूची यहां Circom डॉक्यूमेंटेशन में प्रदान की गई है। ये C-जैसी भाषा (जैसे++,**,<=, आदि) से आने वाले किसी भी व्यक्ति को परिचित लगेंगे। हालांकि, यह ध्यान रखें कि/का अर्थ गुणात्मक प्रतिलोम (multiplicative inverse) के साथ गुणा करना है, और\का अर्थ पूर्णांक विभाजन (integer division) है। - हालांकि, signals के लिए केवल मान्य ऑपरेटर
+,*,===,<--, और<==हैं। हम बाद के लेख में<--और<==पर चर्चा करेंगे।
If statements
Circom हमें if statements का उपयोग करके सशर्त रूप से constraints बनाने की अनुमति देता है — लेकिन ये शर्तें नियतात्मक (deterministic) और compile time पर ज्ञात होनी चाहिए। अगला एक उदाहरण दिखाया गया है:
Example: सम इंडेक्स (Even Indexes) पर समानता लागू करना
मान लीजिए कि हमारे पास दो arrays हैं। हम उन constraints को उत्पन्न करने के लिए निम्नलिखित template का उपयोग कर सकते हैं जो यह लागू करते हैं कि सम (even) इंडेक्स वाले आइटम समान हैं (विषम की जाँच किए बिना):
template EqualOnEven(n) {
signal input in1[n];
signal input in2[n];
for (var i = 0; i < n; i++) {
if (i % 2 == 0) {
in1[i] === in2[i];
}
// otherwise no constraint is generated
}
}
ध्यान दें कि variable i यह तय करता है कि कौन से constraints उत्पन्न होते हैं।
Signals का उपयोग if statements या for loops में ब्रांचिंग कंडीशन के लिए नहीं किया जा सकता है
निम्नलिखित कोड की अनुमति नहीं है क्योंकि signal a का उपयोग if statement के लिए कंडीशनल के रूप में किया जाता है:
template IfStatementViolation() {
signal input a;
signal input b;
if (a == 2) {
b === 3;
}
else {
b === 4;
}
}
Rank 1 Constraint System में, signals के बीच केवल जोड़ (addition) और गुणा (multiplication) हो सकता है। Circom केवल Rank 1 Constraint System के ऊपर एक थिन रैपर (thin wrapper) है। इसलिए, यह एक if statement का जोड़ और गुणा में “अनुवाद” (translate) नहीं कर सकता है।
Circom में signals के आधार पर एक कंडीशनल ऑपरेशन (if statement) करना अभी भी संभव है — यह बाद के एक अध्याय का विषय है। लेकिन अभी के लिए, यह मान लें कि if statement से एकल गुणा (single multiplication) में कोई “प्रत्यक्ष” (direct) अनुवाद नहीं है।
Constraints के भाग के रूप में Variables का उपयोग करना
Variables का उपयोग constraints के हिस्से के रूप में किया जा सकता है। नीचे दिए गए उदाहरण में, हम लागू करते हैं कि input array in[n] एक Fibonacci अनुक्रम (sequence) है। ध्यान दें कि एक variable array syntax var varName[size] है:
template IsFib(n) {
assert(n > 1);
signal input in[n];
// generate the Fibonacci sequence
var correctFibo[n];
correctFibo[0] = 0;
correctFibo[1] = 1;
for (var i = 2; i < n; i++) {
correctFibo[i] = correctFibo[i - 1] + correctFibo[i - 2];
}
// assert that the input is a Fibonacci sequence
for (var i = 0; i < n; i++) {
in[i] === correctFibo[i];
}
}
ध्यान देने योग्य बातें:
assert(n > 1)कोई constraints उत्पन्न नहीं करता है। यदि template parameter के लिए शर्त पूरी नहीं होती है तो यह template को instantiate होने से रोकता है।- हम
signal === varकरके यह लागू कर सकते हैं कि किसी signal का एक निश्चित मान है। यहsignal === 5या किसी अन्य स्थिरांक (constant) के समान है।
Circom में कोई Constant Keyword नहीं है
इसके बजाय, हम पठनीयता (readability) को बेहतर बनाने के लिए किसी मैजिक नंबर (magic number) को एक नाम असाइन करने के लिए variables का उपयोग कर सकते हैं। उदाहरण के लिए:
template Equality() {
signal input in[2];
var left = 0;
var right = 1;
// require the inputs
// to be equal
in[left] === in[right];
}
Variables को अन्य Signals के साथ जोड़ा और गुणा किया जा सकता है
Circom में, constants की तरह ही variables को signals के साथ जोड़ा या गुणा किया जा सकता है। नीचे दिए गए उदाहरण में, हमें यह आवश्यक है कि in2[], in1[] को उसके इंडेक्स से गुणा करके प्राप्त हो।
उदाहरण के लिए, यदि in1[] = [3,5,6] है तो यह आवश्यक है कि in2[] = [0,5,12] हो क्योंकि [3,5,6] को तत्व-वार (element-wise) [0,1,2] से गुणा किया जाता है।
template IsIndexMultiplied(n) {
signal input in1[n];
signal input in2[n];
for (var i = 0; i < n; i++) {
in1[i] * i === in2[i];
}
}
component main = IsIndexMultiplied(3);
/* INPUT = {"in1": [0,1,2], "in2": [0,1,4]} */
// accept
// in1[] = [0,1,2]
// in2[] = [0,1,4]
// reject
// in1[] = [0,1,2]
// in2[] = [0,0,2]
आप कोड का परीक्षण यहां कर सकते हैं।
मुख्य बातें
- पर्दे के पीछे (Behind the scenes), यदि variables को किसी signal के साथ जोड़ा या गुणा किया जाता है, तो variable R1CS में एक स्थिरांक (constant) के रूप में संकलित (compiled) हो जाता है।
- signals के लिए, जोड़, घटाव या गुणा के अलावा अन्य संचालन करने की अनुमति नहीं है क्योंकि एक R1CS में केवल एक स्थिरांक के साथ जोड़ या गुणा हो सकता है। पर्दे के पीछे घटाव वास्तव में एडिटिव इनवर्स (additive inverse) के साथ सिर्फ एक जोड़ है।
- यदि किसी signal को एक स्थिरांक (या स्थिरांक धारण करने वाले variable) से विभाजित किया जाता है, तो यह उस signal को स्थिरांक के गुणात्मक प्रतिलोम (multiplicative inverse) से गुणा करेगा, जब तक कि स्थिरांक 0 न हो, जिस स्थिति में कोड संकलित (compile) नहीं होगा।
अभ्यास प्रश्न
ZK Puzzles से निम्नलिखित प्रश्नों को हल करने का प्रयास करें। अपना उत्तर जाँचने के लिए टेस्ट रन करें।