Circom Constraints
एक Rank 1 Constraint System में प्रति constraint signals के बीच अधिकतम एक multiplication (गुणा) होता है। इसे “quadratic” constraint कहा जाता है। कोई भी constraint जिसमें addition (जोड़) या multiplication के अलावा कोई अन्य operation होता है, उसे Circom द्वारा “Non quadratic constraints are not allowed” error के साथ reject कर दिया जाएगा।
निम्नलिखित दो उदाहरण compile नहीं होंगे क्योंकि उनमें प्रति constraint signals के एक से अधिक multiplication हैं।
Non quadratic constraint example 1
निम्नलिखित को compile करने पर यह error आएगा error: [T3001]: Non quadratic constraints are not allowe
template QuadraticViolation1() {
signal input a;
signal input b;
signal input c;
signal input d;
// two multiplications per constraint
// is not allowed
a * b === c * d;
}
Non quadratic constraint example 2
पिछले उदाहरण के समान, निम्नलिखित constraint में signals के बीच दो multiplications हैं।
template QuadraticViolation2() {
signal input a;
signal input b;
signal input c;
signal input d;
// two multiplications per constraint
// is not allowed
a * b * c === d;
}
Constant multiplications गिने नहीं जाते हैं
इसलिए, निम्नलिखित उदाहरण compile होंगे, भले ही उनमें एक से अधिक multiplication हों।
a * b === c;
2*a * 3*b === 4*c; // integer coefficients allowed
a * b + c === d; // addition and one multiplication allowed
a + b + c === d; // multiplication is optional
a * b + c === d + e + f; // no restrictions on number of additions
Quadratic Form और R1CS
याद करें कि arithmetization में, हम अपने verification program को intermediate steps की एक श्रृंखला में flatten करते हैं, जहां प्रत्येक intermediate step में unknown variables के बीच केवल एक ही multiplication होता है।
निम्नलिखित verification उदाहरण पर विचार करें:
def someProblem(x, y, out):
res = y^2 + 4*(x^2)*y -2
assert out == res, "incorrect inputs";
एक R1CS में conversion करने पर हमें यह प्राप्त होगा:
v1 === y * y
v2 === x * x
out === v1 + (4v2 * y) - 2
- R1CS format के लिए हमें समस्या को ऐसे intermediate steps में restructure करने की आवश्यकता होती है जिनमें quadratic constraint की सीमा का पालन करने के लिए signals के बीच केवल 1 multiplication operation हो।
- यह हमारे constraints का system बनाता है।
परिणामस्वरूप, R1CS representation इस प्रकार होगा:
// Cw = Aw * Bw
v1 = y * y
v2 = x * x
out -v1 +2 = (4v2 * y)
चूंकि हमने पहले यह सुनिश्चित किया था कि प्रति constraint केवल 1 multiplication हो, इसलिए हम constraints के system को vector form में व्यक्त करने में सक्षम हैं, जो कि एक R1CS है।
Non-multiplicative Operators के उदाहरण जो Non quadratic Constraint का कारण बनते हैं
यदि किसी constraint में एक illegal operation (जो addition या multiplication नहीं है) का उपयोग किया जाता है, तो Circom compiler “Non quadratic constraints are not allowed!” error रिपोर्ट करेगा।
यहां, हम कुछ उदाहरण प्रदान कर रहे हैं।
Example 1: Signal Arrays को Index करने के लिए Signals का उपयोग नहीं किया जा सकता
निम्नलिखित operation के परिणामस्वरूप quadratic constraints का उल्लंघन (violation) होगा। Array indexing से addition और multiplication में कोई सीधा translation नहीं है। निम्नलिखित कोड से यह error आता है Non-quadratic constraint was detected statically, using unknown index will cause the constraint to be non-quadratic:
template KMustEqual5(n) {
signal input in[n];
signal input k;
// not allowed
in[k] === 5;
}
तकनीकी रूप से अभी भी array indexing को पूरा करना संभव है, लेकिन इसके लिए एक अधिक जटिल समाधान की आवश्यकता होती है जिसे हम बाद के अध्याय में दिखाएंगे।
Example 2: Signals % और << जैसे Operations का उपयोग नहीं कर सकते
निम्नलिखित constraints एक “Non quadratic constraints are not allowed!” violation उत्पन्न करेंगे:
template Example() {
signal input a;
signal input b;
// not allowed
a === b % 5;
// not allowed
a === b << 2;
}
Circom division को कैसे handle करता है
थोड़ा सूक्ष्म रूप से, Circom एक constant द्वारा “division” (भाग) की अनुमति देगा, क्योंकि इसे बस उस संख्या के multiplicative inverse द्वारा multiplication से replace किया जा सकता है। इसलिए, निम्नलिखित कोड valid है:
template Example() {
signal input a;
signal input b;
a === b / 2;
}
component main = Example();
हालाँकि, signals को divide करने की अनुमति नहीं है क्योंकि इसका मतलब है कि हमने signal के multiplicative inverse की गणना की है, जिसका केवल addition और multiplication में कोई सीधा translation नहीं है। Multiplicative inverse की गणना आमतौर पर extended euclidean algorithm के साथ की जाती है, जिसके लिए loops और conditional statements की आवश्यकता होती है — ऐसे operations जिन्हें मूल रूप से addition और multiplication के साथ व्यक्त नहीं किया जा सकता है।
template Example() {
signal input a;
signal input b;
signal input c;
// not allowed
a === b / c;
}
component main = Example();
इसके विपरीत, signals के subtraction (घटाव) की अनुमति है क्योंकि यह सीधे constant -1 द्वारा multiplication में translate हो जाता है:
template Example() {
signal input a;
signal input b;
// allowed
a === b - a;
// equivalent
a === b + -1*a
}
component main = Example();
Modular inverse द्वारा multiplication के विपरीत, Integer division को \ द्वारा दर्शाया जाता है और इसे signals पर लागू करने की अनुमति नहीं है:
template Example() {
signal input a;
signal input b;
// can only use \ with variables
// not signals
a === b \ 2;
}
component main = Example();
Variables के लिए, आपके पास integer division और “normal” division (यानी, divisor के multiplicative inverse के साथ multiplication) दोनों होते हैं।
दूसरी ओर, signals के लिए केवल “normal” division (उपरोक्त अर्थ में) की अनुमति है।
सारांश
एक constraint में signals के बीच केवल एक multiplication हो सकता है, लेकिन additions की संख्या पर कोई सीमा नहीं है।
ऐसा लग सकता है कि यह प्रतिबंध सरल अंकगणित से परे किसी भी दिलचस्प computation को व्यक्त करना असंभव बना देता है, लेकिन हम इस ट्यूटोरियल श्रृंखला में बाद में देखेंगे कि इस सीमा से निपटने के लिए कई चतुर (clever) design patterns मौजूद हैं।
एक बार जब हम design patterns को समझ लेते हैं, तो हम बहुत अधिक जटिल algorithms को मॉडल करने के लिए उन्हें compose कर सकते हैं।