Circom में एक सार्वजनिक इनपुट witness में एक signal है जिसे verifier के सामने प्रकट किया जाएगा।
उदाहरण के लिए, मान लें कि हम एक ZK proof बनाना चाहते हैं जो यह बताता है: “हम एक हैश के इनपुट को जानते हैं जिसने 0x492c…9254 उत्पन्न किया है।” इस दावे को सार्थक बनाने के लिए, मान 0x492c…9254 (लक्षित हैश आउटपुट) को सार्वजनिक होना चाहिए। अन्यथा, हम शब्दार्थ (semantically) रूप से यह दावा कर रहे हैं कि “हमने कुछ हैश किया है,” जो उतना उपयोगी नहीं है।
निम्नलिखित सर्किट दावा करता है, “मैंने दो संख्याओं को एक साथ गुणा किया और तीसरी संख्या प्राप्त की:”
template Main() {
signal input a;
signal input b;
signal input c;
a * b === c;
}
component main = Main();
अगला सर्किट भी ऐसा ही दावा करता है, लेकिन इस बदलाव के साथ कि परिणाम सार्वजनिक है “मैंने दो संख्याओं को एक साथ गुणा किया और तीसरी प्राप्त की जिसका मान सार्वजनिक रूप से ज्ञात है:”
template Main() {
signal input a;
signal input b;
signal input c;
a * b === c;
}
component main {public [c]} = Main();
- डिफ़ॉल्ट रूप से सभी इनपुट signal निजी (private) होते हैं जब तक कि उन्हें स्पष्ट रूप से
component main {public [c]}सिंटैक्स का उपयोग करके सार्वजनिक न किया जाए। main component ही एकमात्र स्थान है जहाँ हम यह परिभाषित कर सकते हैं कि कौन से इनपुट सार्वजनिक हैं। - सूची
[c]सार्वजनिक किए जाने वाले signal की एक सूची है। इसमें अधिक signal हो सकते थे, जैसे कि[a,c], यदि हमaको भी सार्वजनिक करना चाहते। - केवल इनपुट signal को सार्वजनिक रूप से निर्दिष्ट किया जा सकता है, मध्यवर्ती (intermediate) signal को नहीं।
उपरोक्त टेम्प्लेट एक Rank-1 Constraint System (R1CS) में संकलित (compile) होता है जो निम्नलिखित के समान है, जहाँ हम main component में output कीवर्ड पेश करते हैं:
template Main() {
signal input a;
signal input b;
signal output c;
a * b ==> c;
}
component main = Main();
ऊपर दिए गए दोनों टेम्प्लेट में, c सार्वजनिक है और इसे a और b का गुणनफल होने के लिए बाध्य (constrained) किया गया है। इसलिए, अंतर्निहित R1CS समान है। हालाँकि, दूसरा संस्करण अधिक “सुविधाजनक” है क्योंकि हमें स्पष्ट रूप से c प्रदान नहीं करना पड़ता है। पहले सर्किट में जो component main {public [c]} का उपयोग करता है, यदि हम c प्रदान करते हैं जो बाधाओं (constraints) का पालन नहीं करता है, तो witness उत्पन्न नहीं होगा। हालाँकि, दूसरे सर्किट में c को एक आउटपुट के रूप में उपयोग करने पर, witness जनरेटर स्वचालित रूप से c के लिए सही मान की गणना करता है, जिससे मैन्युअल इनपुट की आवश्यकता समाप्त हो जाती है।
चूँकि c पूरी तरह से a और b द्वारा निर्धारित होता है, इसलिए c के लिए स्पष्ट रूप से कोई मान प्रदान करने का कोई कारण नहीं है, इसलिए output नोटेशन को प्राथमिकता दी जानी चाहिए।
ध्यान दें कि आउटपुट सार्वजनिक (public) होते हैं।
इनपुट के मामले में, यदि हम कुछ को सार्वजनिक करना चाहते हैं, तो इसका मतलब है कि हमारे पास एक ऐसा signal है जिसका मान पूरी तरह से अन्य signal मानों द्वारा निर्धारित नहीं होता है। ऐसे मामलों में, हमें public मॉडिफायर विधि का उपयोग करना चाहिए। उदाहरण के लिए, यदि हम दावा करते हैं “मैंने a, b, और c को एक साथ गुणा करके d प्राप्त किया, जिसमें a और d सार्वजनिक हैं, लेकिन b और c निजी हैं,” तो हम उस सर्किट की संरचना इस प्रकार करेंगे:
template Main() {
signal input a; // explicitly public
signal input b;
signal input c;
signal output d; // implicitly public
signal s <== a * b; // intermediate signal
d <== c * s;
}
component main{public [a]} = Main();
यहाँ बताया गया है कि output signal को कैसे समझें:
- एक सब-कंपोनेंट के लिए, एक
outputवह signal है जिसे अन्य इनपुट से एक मान असाइन किया जाएगा और संभावित रूप से बाद में उस कंपोनेंट द्वारा उपयोग किया जाएगा जो सब-कंपोनेंट को इंस्टेंटिएट (instantiate) करता है। - main component के लिए, एक
outputwitness में एक सार्वजनिक signal है जिसका मान पूरी तरह से अन्य इनपुट signal द्वारा निर्धारित किया जाना चाहिए। एक आउटपुट signal घोषित करना और उसे कोई मान निर्दिष्ट न करना एक भेद्यता (vulnerability) पैदा कर सकता है क्योंकि एक prover जो चाहे वह मान निर्दिष्ट कर सकता है। हम आगामी अध्याय में इस शोषण (exploit) के तंत्र को दिखाएंगे।
“आउटपुट” नाम होने के बावजूद, main component से “आउटपुट” प्राप्त करने का कोई तंत्र नहीं है — Circom कुछ भी वापस (return) नहीं कर सकता है। किसी अन्य कोडबेस के लिए “आउटपुट” का मान पढ़ने का कोई तरीका नहीं है।
यह केवल एक R1CS उत्पन्न करता है, और R1CS के लिए witness की गणना करने में मदद करता है। Snarkjs फिर एक ZK proof उत्पन्न करने के लिए Circom कोड का उपयोग करता है जो यह प्रमाणित करता है कि witness R1CS को संतुष्ट करता है।
Circom को “एक्जीक्यूट (execute)” नहीं किया जा रहा है, यही कारण है कि यह कुछ भी “रिटर्न (return)” नहीं करता है। आप Circom को “रन (run)” नहीं कर रहे हैं, आप केवल एक अमूर्त (abstract) सर्किट का वर्णन कर रहे हैं जिसे दो भागों में बदला जा रहा है: R1CS और एक witness जनरेटर, जिनका उपयोग अलग-अलग किया जाता है।
main component में एक आउटपुट signal को एक मध्यवर्ती (intermediate) signal के रूप में माना जा सकता है जो verifier के लिए सार्वजनिक है।
सार्वजनिक signal के साथ Witness लेआउट
Circom witness वेक्टर को इस प्रकार व्यवस्थित करता है:
[constant, public signals, private signals]
आइए एक उदाहरण के रूप में इसका उपयोग करें: “मैंने छिपे हुए मानों a, b को एक सार्वजनिक मान c के साथ गुणा करके d का सार्वजनिक मान प्राप्त किया:”
// assert that a*b === c*d
template Example() {
signal input a;
signal input b;
signal input c;
signal input d;
signal s;
s <== a * b;
d === s * c;
}
component main {public [c, d]} = Example();
ध्यान दें कि हम d को आउटपुट बनाकर कुछ कोड बचा सकते थे, लेकिन आगामी प्रदर्शन को अधिक स्पष्ट बनाने के लिए हम यहाँ ऐसा नहीं करते हैं।
यह देखने के लिए कि witness की संरचना कैसे की जाती है:
- उपरोक्त फ़ाइल को
Example.circomके रूप में सहेजें - इसे
circom Example.circom --sym --r1cs --wasmके साथ संकलित (Compile) करें input.jsonबनाएँ:echo '{"a": "3", "b": "4", "c":"2", "d":"24"}' > input.jsoncd example_js- witness की गणना करें:
node generate_witness.js example.wasm ../input.json witness.wtns - witness को json में बदलें और उसे cat करें:
snarkjs wej witness.wtns && cat witness.json
हमें निम्नलिखित परिणाम प्राप्त होना चाहिए। ध्यान दें कि यह हमारे द्वारा input.json के लिए प्रदान किए गए मानों से मेल खाता है:
[
"1", // constant
"2", // c (public signal)
"24", // d (public signal)
"3", // a
"4", // b
"12" // s
]
इस प्रकार, हम देख सकते हैं कि witness का लेआउट हमेशा ऐसा होता है:
- witness में constant प्रविष्टि (जो हमेशा 1 होती है)
- सार्वजनिक signal (
c,d) - इनपुट signal (
a,b) - मध्यवर्ती (intermediate) signal (
s)
सारांश
- इनपुट डिफ़ॉल्ट रूप से निजी (private) होते हैं
- हम
component main {public [in1, in2]} = Main();सिंटैक्स का उपयोग करके इनपुट को सार्वजनिक बना सकते हैं - आउटपुट सार्वजनिक signal होते हैं
- आउटपुट वे signal हैं जिनकी गणना अन्य इनपुट के आधार पर उपयोगकर्ता के लिए की जाती है