Groth16 एल्गोरिथम एक prover को एक trusted setup में प्राप्त elliptic curve points पर एक quadratic arithmetic program की गणना करने में सक्षम बनाता है, जिसे एक verifier द्वारा जल्दी से चेक किया जा सकता है। यह जाली (forged) proofs को रोकने के लिए trusted setup से auxiliary elliptic curve points का उपयोग करता है।
हम G1 elliptic curve group से संबंधित एक elliptic curve point को [x]1 के रूप में और G2 elliptic curve group से संबंधित एक elliptic curve point को [x]2 के रूप में संदर्भित करते हैं। [x]1 और [x]2 के बीच एक pairing को [x]1∙[x]2 के रूप में दर्शाया जाता है और यह G12 में एक element उत्पन्न करता है। बोल्ड में दिए गए variables जैसे कि a वेक्टर्स (vectors) हैं, अपर केस बोल्ड अक्षर जैसे कि L मैट्रिसेस (matrices) हैं, और फील्ड एलिमेंट्स (field elements) (जिन्हें कभी-कभी अनौपचारिक रूप से “scalars” कहा जाता है) लोअर केस अक्षर हैं जैसे कि d। सभी अंकगणितीय संचालन (arithmetic operations) एक finite field में होते हैं जिसकी characteristic elliptic curve group के order के बराबर होती है।
एक Arithmetic Circuit (ZK Circuit) दिए जाने पर, हम इसे एक Rank 1 Constraint System (R1CS)La∘Ra=Oa में परिवर्तित करते हैं, जिसमें मैट्रिसेस का आयाम (dimension) n पंक्तियाँ (rows) और m कॉलम (columns) होता है और एक witness vector a होता है। फिर, हम मैट्रिसेस के कॉलम को x मानों [1,2,...,n] पर y मानों के रूप में इंटरपोलेट (interpolate) करके R1CS को Quadratic Arithmetic Program (QAP) में बदल सकते हैं। चूँकि L, R, और O में m कॉलम हैं, इसलिए हमारे पास m पॉलीनोमियल्स (polynomials) के तीन सेट होंगे:
u1(x),...,um(x)v1(x),...,vm(x)w1(x),...,wm(x)m polynomials interpolated on the m columns of Lm polynomials interpolated on the m columns of Rm polynomials interpolated on the m columns of O
इससे, हम एक Quadratic Arithmetic Program (QAP) का निर्माण कर सकते हैं:
यदि कोई तीसरा पक्ष powers of tau सेरेमनी (ceremony) के माध्यम से एक structured reference string (srs) बनाता है, तो prover QAP में sum terms (∑aifi(x) terms) का मूल्यांकन एक छिपे हुए बिंदु τ पर कर सकता है। मान लें कि structured reference strings की गणना इस प्रकार की जाती है:
[Ωn−1,Ωn−2,…,Ω2,Ω1,G1][Θn−1,Θn−2,…,Θ2,Θ1,G2][Υn−2,Υn−3,…,Υ1,Υ0]=[τnG1,τn−1G1,…,τG1,G1]=[τnG2,τn−1G2,…,τG2,G2]=[τn−2t(τ)G1,τn−3t(τ)G1,…,τt(τ)G1,t(τ)G1]srs for G1srs for G2srs for h(τ)t(τ)
हम f(τ) को inner product के माध्यम से एक structured reference string [τdG1,...,τ2G1,τG1,G1] पर मूल्यांकित (evaluated) एक पॉलीनोमियल के रूप में संदर्भित करते हैं:
f(τ) उपरोक्त अभिव्यक्ति (expression) के लिए एक शॉर्टहैंड है, और यह एक elliptic curve point उत्पन्न करता है। इसका मतलब यह नहीं है कि prover τ को जानता है।
Prover निम्नलिखित गणना करके trusted setup पर अपने QAP का मूल्यांकन कर सकता है:
यदि QAP संतुलित (balanced) है, तो निम्नलिखित समीकरण लागू होता है:
[A]1∙[B]2=?[C]1∙G2
उद्देश्य (Motivation)
केवल ([A]1,[B]2,[C]1) को प्रस्तुत करना यह साबित करने के लिए कोई ठोस तर्क नहीं है कि prover a को जानता है जिससे कि QAP संतुलित हो।
Prover आसानी से a, b, c मानों का आविष्कार कर सकता है जहाँ ab=c हो, और गणना कर सकता है
[A]1[B]2[C]1=aG1=bG2=cG1
और उन्हें verifier के सामने elliptic curve points [A]1, [B]2, [C]1 के रूप में प्रस्तुत कर सकता है।
इस प्रकार, verifier को यह पता नहीं चल पाता है कि क्या ([A]1,[B]2,[C]1) एक संतुष्ट (satisfied) QAP का परिणाम थे या नहीं।
हमें prover को ईमानदार रहने के लिए मजबूर करने की आवश्यकता है, वह भी बिना बहुत अधिक computational overhead जोड़े। इसे पूरा करने वाला पहला एल्गोरिथम “Pinocchio: Nearly Practical Verifiable Computation” था। यह इतना उपयोगी था कि ZCash ने अपने ब्लॉकचेन का पहला संस्करण इसी पर आधारित किया।
हालाँकि, Groth16 इसी काम को बहुत कम चरणों में पूरा करने में सक्षम था, और यह एल्गोरिथम आज भी व्यापक रूप से उपयोग में है, क्योंकि इसके बाद किसी भी एल्गोरिथम ने verification चरण के लिए इतना कुशल एल्गोरिथम तैयार नहीं किया है (यद्यपि अन्य एल्गोरिदम ने trusted setup को हटा दिया है या prover के लिए काम की मात्रा को काफी कम कर दिया है)।
2024 के लिए अपडेट: Cryptology में प्रकाशित एक पेपर जिसका काफी उत्साहपूर्ण शीर्षक “Polymath: Groth16 is not the limit” है, एक ऐसे एल्गोरिथम को प्रदर्शित करता है जिसमें Groth16 की तुलना में कम verifier चरणों की आवश्यकता होती है। हालाँकि, इस लेख को लिखे जाने के समय तक इस एल्गोरिथम का कोई ज्ञात कार्यान्वयन (implementation) नहीं है।
जालसाजी को रोकना भाग 1: α और β का परिचय
एक “unsolveable” (न सुलझाया जा सकने वाला) verification फॉर्मूला
मान लीजिए कि हम अपने verification फॉर्मूले को निम्नलिखित में अपडेट करते हैं:
[A]1∙[B]2=?[D]12+[C]1∙G2
ध्यान दें कि हम सुविधा के लिए G12 समूह के लिए additive notation का उपयोग कर रहे हैं।
यहाँ, [D]12, G12 का एक element है और इसका एक अज्ञात discrete logarithm है।
अब हम यह दिखाते हैं कि [D]12 के discrete logarithm को जाने बिना, एक verifier के लिए इस समीकरण का हल ([A]1,[B]2,[C]1) प्रदान करना असंभव है।
अटैक 1: A और B की जालसाजी (Forging) करना और C प्राप्त करना
मान लीजिए कि prover [A]1 और [B]2 उत्पन्न करने के लिए बेतरतीब ढंग से (randomly) a’ और b’ का चयन करता है और एक ऐसा मान [C’] प्राप्त करने का प्रयास करता है जो verifier के फॉर्मूले के अनुकूल हो।
[A]1∙[B]2=?[D]12+[C]1∙G2
[A]1 और [B]2 के discrete logarithms को जानते हुए, दुर्भावनापूर्ण (malicious) prover निम्नलिखित करके [C’] को हल करने का प्रयास करता है:
अंतिम पंक्ति में prover को χ12 के discrete log के लिए हल करने की आवश्यकता होती है, इसलिए वे [C′]1 के लिए एक वैध (valid) discrete log की गणना नहीं कर सकते हैं।
अटैक 2: C की जालसाजी (Forging) करना और A और B प्राप्त करना
यहाँ prover एक यादृच्छिक (random) बिंदु c′ चुनता है और [C′]1 की गणना करता है। चूँकि वे c′ को जानते हैं, इसलिए वे a′ और b′ के एक ऐसे अनुकूल संयोजन को खोजने का प्रयास कर सकते हैं जहाँ:
इसके लिए prover को, [ζ]12 दिए जाने पर, एक ऐसा [A]1 और [B]2 खोजना होगा जो पेयर (pair) होने पर [ζ]12 उत्पन्न करे।
Discrete log समस्या के समान, हम अप्रमाणित क्रिप्टोग्राफ़िक मान्यताओं (cryptographic assumptions) पर भरोसा करते हैं कि यह गणना (G12 में एक element को G1 और G2 element में डिकम्पोज़ (decompose) करना) अव्यावहारिक (infeasible) है। इस मामले में, यह मान्यता कि हम [ζ]12 को [A]1 और [B]2 में डिकम्पोज़ नहीं कर सकते, Bilinear Diffie-Hellman Assumption कहलाती है। इच्छुक पाठक Decisional Diffie-Hellman Assumption पर एक संबंधित चर्चा देख सकते हैं।
(अप्रमाणित का अर्थ अविश्वसनीय नहीं है। यदि आप इस मान्यता को सिद्ध करने या गलत साबित करने का कोई तरीका खोज सकते हैं, तो नाम और पैसा आपका इंतजार कर रहा है! व्यवहार में, [ζ]12 को [A]1 और [B]2 में डिकम्पोज़ करने का कोई ज्ञात तरीका नहीं है और इसे कम्प्यूटेशनल रूप से अव्यावहारिक (computationally infeasible) माना जाता है।)
α और β का उपयोग कैसे किया जाता है
व्यवहार में, Groth16 [D]12 टर्म का उपयोग नहीं करता है। इसके बजाय, trusted setup दो यादृच्छिक (random) scalars α और β उत्पन्न करता है और निम्नलिखित रूप में गणना किए गए elliptic curve points ([α]1,[β]2) प्रकाशित करता है:
[α]1[β]2=αG1=βG2
जिसे हमने [D]12 के रूप में संदर्भित किया था, वह वास्तव में [α]1∙[β]2 है।
Proving और verification फ़ॉर्मूलों को फिर से प्राप्त करना (Re-deriving)
Verification फॉर्मूले [A]1∙[B]2=?[α]1∙[β]2+[C]1∙G2 को “सुलझाने योग्य (solvable)” बनाने के लिए, हमें α और β को शामिल करने के लिए अपने QAP फॉर्मूले को बदलना होगा।
हम आगे क्या करने जा रहे हैं, इसकी एक झलक के रूप में: यदि हम θ को [α]1 से और η को [β]2 से बदल दें, तो हमें पहले का अपडेटेड verification फॉर्मूला मिलता है:
Prover τ, α, या β को जाने बिना [A]1 और [B]2 की गणना कर सकता है। Structured reference string (powers of τ) और elliptic curve points ([α]1,[β]2) दिए जाने पर, prover [A]1 और [B]2 की गणना इस प्रकार करता है:
यहाँ, aiui(τ) का मतलब यह नहीं है कि prover τ को जानता है। Prover i=1,2,…,m के लिए ui(τ) की गणना करने हेतु structure reference string [τn−1G1,τn−2G1,…,τG1,G1] का उपयोग कर रहा है, और [B]2 के लिए G2 srs का उपयोग कर रहा है।
हालाँकि, वर्तमान में α और β को जाने बिना [C]1 की गणना करना संभव नहीं है। Prover [α]1 को ∑aiui(τ) के साथ और [β]2 को ∑aivi(τ) के साथ पेयर (pair) नहीं कर सकता क्योंकि इससे एक G12 बिंदु बन जाएगा, जबकि prover को [C]1 के लिए एक G1 बिंदु की आवश्यकता होती है।
इसके बजाय, trusted setup को विस्तारित QAP के समस्याग्रस्त C टर्म के लिए m पॉलीनोमियल्स की पूर्व-गणना (precompute) करने की आवश्यकता होती है।
αi=1∑maivi(τ)+βi=1∑maiui(τ)+i=1∑maiwi(τ)
कुछ बीजगणितीय हेरफेर (algebraic manipulation) के साथ, हम sum terms को एक एकल (single) sum में जोड़ते हैं:
=i=1∑m(αaivi(τ)+βaiui(τ)+aiwi(τ))
और ai को फैक्टर आउट (factor out) करते हैं:
=i=1∑mai(αvi(τ)+βui(τ)+wi(τ))
Trusted setup ऊपर दिए गए बॉक्स वाले टर्म से τ पर मूल्यांकित m पॉलीनोमियल्स बना सकता है, और prover sum की गणना करने के लिए उसका उपयोग कर सकता है। सटीक विवरण अगले अनुभाग में दिखाए गए हैं।
अब तक के एल्गोरिथम का सारांश
Trusted setup के चरण
ठोस रूप में, trusted setup निम्नलिखित की गणना करता है:
α,β,τ[τn−1G1,τn−2G1,…,τG1,G1][τn−1G2,τn−2G2,…,τG2,G2][τn−2t(τ)G1,τn−3t(τ)G1,…,τt(τ)G1,t(τ)G1][Ψ1]1[Ψ2]1[Ψm]1←random scalars←srs for G1←srs for G2←srs for h(τ)t(τ)=(αv1(τ)+βu1(τ)+w1(τ))G1=(αv2(τ)+βu2(τ)+w2(τ))G1⋮=(αvm(τ)+βum(τ)+wm(τ))G1
Trusted setup प्रकाशित करता है:
([α]1,[β]2,srsG1,srsG2,srs for h(τ)t(τ),[Ψ1]1,[Ψ2]1,…,[Ψm]1)
ध्यान दें कि हमने “समस्याग्रस्त (problematic)” पॉलीनोमियल
=i=1∑mai(αvi(τ)+βui(τ)+wi(τ))
(जिसमें α और β शामिल थे) को निम्नलिखित से बदल दिया है:
i=1∑mai[Ψi]1
Verifier के चरण
Verifier गणना करता है:
[A]1∙[B]2=?[α]1∙[β]2+[C]1∙G2
पब्लिक इनपुट्स (Public inputs) का समर्थन करना
अब तक का verifier फॉर्मूला पब्लिक इनपुट्स का समर्थन नहीं करता है, अर्थात् witness के एक हिस्से को सार्वजनिक (public) बनाना।
परंपरानुसार, witness के सार्वजनिक हिस्से वेक्टर a के पहले ℓ एलिमेंट्स होते हैं। उन एलिमेंट्स को सार्वजनिक बनाने के लिए, prover बस उन्हें उजागर (reveal) करता है:
[a1,a2,…,aℓ]
Verifier को यह जांचने के लिए कि वे मान वास्तव में उपयोग किए गए थे, verifier को कुछ ऐसी गणनाएँ करनी होंगी जो मूल रूप से prover कर रहा था।
ध्यान दें कि केवल [C]1 की गणना बदली है – prover केवल ai और Ψi टर्म्स का उपयोग ℓ+1 से m तक करता है।
Verifier sum के पहले ℓ टर्म्स की गणना करता है:
[X]1=i=1∑ℓaiΨi
और verification समीकरण है:
[A]1∙[B]2=?[α]1∙[β]2+[X]1∙G2+[C]1∙G2
भाग 2: γ या δ के साथ पब्लिक इनपुट्स को प्राइवेट इनपुट्स से अलग करना
i≤ℓ के लिए Ψi का दुरुपयोग करके proofs की जालसाजी करना
ऊपर दिए गए समीकरण में यह मान्यता है कि prover [C]1 की गणना करने के लिए केवल Ψℓ+1 से Ψm तक का उपयोग कर रहा है, लेकिन किसी बेईमान (dishonest) prover को [C]1 की गणना करने के लिए Ψ1 से Ψℓ का उपयोग करने से कोई नहीं रोकता, जिससे एक जाली proof बन सकता है।
उदाहरण के लिए, यहाँ हमारा वर्तमान verification समीकरण है:
[A]1∙[B]2=?[α]1∙[β]2+i=1∑ℓaiΨi+[C]1∙G2
यदि हम C टर्म को अंदर से विस्तार (expand) करें, तो हमें निम्नलिखित मिलता है:
हालाँकि, prover को सार्वजनिक witness का एक वैध (valid) हिस्सा [1,2,0] बनाने और शून्य किए गए सार्वजनिक हिस्से को गणना के निजी हिस्से में ले जाने से कोई नहीं रोकता, जैसा कि नीचे दिया गया है:
ऊपर दिया गया समीकरण वैध (valid) है, लेकिन यह आवश्यक नहीं है कि witness मूल constraints को संतुष्ट करता हो।
इसलिए, हमें prover को [C]1 की गणना के भाग के रूप में Ψ1 से Ψℓ का उपयोग करने से रोकना होगा।
γ और/या δ का परिचय
उपरोक्त समस्या से बचने के लिए, trusted setup एक नया scalar : γ या δ पेश करता है ताकि Ψℓ+1 से Ψm तक को Ψ1 से Ψℓ से अलग करने के लिए बाध्य किया जा सके। ऐसा करने के लिए, trusted setup निजी टर्म्स (जो [C]1 बनाते हैं) को δ से और/या सार्वजनिक टर्म्स (जो [X]1 बनाते हैं, वह sum जिसकी verifier गणना करता है) को γ से विभाजित (divide) करता है (अर्थात् modular inverse से गुणा करता है)।
चूँकि h(τ)t(τ) टर्म [C]1 में सन्निहित (embedded) है, इसलिए उन टर्म्स को भी δ से विभाजित करने की आवश्यकता है। यदि δ और γ दोनों में से किसी एक का अज्ञात discrete logarithm है, तो पहले वर्णित जालसाजी के साथ-साथ अन्य संभावित तरीकों से बचा जा सकता है। इस विधि का उपयोग Zcash के Sapling-आधारित trusted setups में किया गया था जहाँ γ को केवल G2 पर छोड़ दिया जाता है और δ को अभी भी बाद के trusted setup चरणों में G2 से एक यादृच्छिक (random) मान में अपडेट किया जाता है।
α,β,τ,γ,δ[τn−1G1,τn−2G1,…,τG1,G1][τn−1G2,τn−2G2,…,τG2,G2][δτn−2t(τ)G1,δτn−3t(τ)G1,…,δτt(τ)G1,δt(τ)G1][Ψ1]1[Ψ2]1[Ψℓ]1[Ψℓ+1]1[Ψℓ+2]1[Ψm]1←random scalars←srs for G1←srs for G2←srs for h(τ)t(τ)public portion of the witness=γαv1(τ)+βu1(τ)+w1(τ)G1=γαv2(τ)+βu2(τ)+w2(τ)G1⋮=γαvℓ(τ)+βuℓ(τ)+wℓ(τ)G1private portion of the witness=δαvℓ+1(τ)+βuℓ+1(τ)+wℓ+1(τ)G1=δαvℓ+2(τ)+βuℓ+2(τ)+wℓ+2(τ)G1⋮=δαvm(τ)+βum(τ)+wm(τ)G1
Trusted setup प्रकाशित करता है:
([α]1,[β]2,[γ]2,[δ]2,srsG1,srsG2,srs for h(τ)t(τ),[Ψ1]1,[Ψ2]1,…,[Ψm]1)
और verifier के चरणों में अब denominators को रद्द (cancel out) करने के लिए [γ]2 और/या [δ]2 द्वारा pairing शामिल है:
[A]1∙[B]2=?[α]1∙[β]2+[X]1∙[γ]2+[C]1∙[δ]2
भाग 3: True zero knowledge लागू करना: r और s
हमारी योजना (scheme) अभी पूरी तरह से zero knowledge नहीं है। यदि कोई हमलावर हमारे witness vector का अनुमान लगाने में सक्षम है (जो कि संभव है यदि वैध (valid) इनपुट की केवल एक छोटी श्रृंखला हो, उदा. privileged addresses से गुप्त मतदान), तो वे अपने बनाए गए proof की तुलना मूल proof से करके सत्यापित कर सकते हैं कि उनका अनुमान सही है।
एक साधारण उदाहरण के रूप में, मान लें कि हमारा दावा है कि x1 और x2 दोनों या तो 0 हैं या 1 हैं। संबंधित arithmetic circuit इस प्रकार होगा:
x1(x1−1)=0x2(x2−1)=0
हमलावर को यह पता लगाने के लिए केवल चार संयोजनों (combinations) का अनुमान लगाने की आवश्यकता है कि witness क्या है। अर्थात्, वे एक witness का अनुमान लगाते हैं, एक proof उत्पन्न करते हैं, और देखते हैं कि क्या उनका उत्तर मूल proof से मेल खाता है।
अनुमान लगाने से रोकने के लिए, prover को अपने proof में “salt” (यादच्छिक डेटा) मिलाना होगा, और salt को समायोजित करने के लिए verification समीकरण को संशोधित करने की आवश्यकता होगी।
Prover दो यादृच्छिक (random) field elements r और s को सैंपल करता है और उन्हें A और B में जोड़ता है ताकि witness का अनुमान न लगाया जा सके – एक हमलावर को witness और salts r तथा s दोनों का अनुमान लगाना होगा:
अंतिम verification फॉर्मूला प्राप्त करने के लिए, आइए अस्थायी रूप से इस बात को अनदेखा कर दें कि हम ग्रीक अक्षर वाले टर्म्स के discrete logs को नहीं जानते हैं, और verification समीकरण AB के बाएँ पक्ष (left-hand-side) की गणना करें:
हम Asδ और Brδ के संदर्भ में लिखने के लिए रेखांकित (underlined) टर्म्स को निम्नानुसार पुनर्व्यवस्थित करते हैं। हम rδsδ को rsδ2+rsδ2−rsδ2 में भी विभाजित (split) करते हैं:
अब हम इस समीकरण को verifier और prover के हिस्सों में अलग करते हैं। बॉक्स वाले टर्म्स verifier का हिस्सा हैं, और underbrace वाले टर्म्स वे हैं जो prover प्रदान करता है:
अब हम शुरू से अंत तक (end-to-end) Groth16 एल्गोरिथम दिखाने के लिए तैयार हैं। Trusted setup और verification चरण पिछले उदाहरण से अपरिवर्तित रहते हैं जहाँ हमने γ और δ को शामिल किया था। r और s को शामिल करने के लिए केवल prover की गणना बदलती है।
Trusted Setup
α,β,τ,γ,δ[τn−1G1,τn−2G1,…,τG1,G1][τn−1G2,τn−2G2,…,τG2,G2][δτn−2t(τ)G1,δτn−3t(τ)G1,…,δτt(τ)G1,δt(τ)G1][Ψ1]1[Ψ2]1[Ψℓ]1[Ψℓ+1]1[Ψℓ+2]1[Ψm]1←random scalars←srs for G1←srs for G2←srs for h(τ)t(τ)public portion of the witness=γαv1(τ)+βu1(τ)+w1(τ)G1=γαv2(τ)+βu2(τ)+w2(τ)G1⋮=γαvℓ(τ)+βuℓ(τ)+wℓ(τ)G1private portion of the witness=δαvℓ+1(τ)+βuℓ+1(τ)+wℓ+1(τ)G1=δαvℓ+2(τ)+βuℓ+2(τ)+wℓ+2(τ)G1⋮=δαvm(τ)+βum(τ)+wm(τ)G1
Trusted setup प्रकाशित करता है:
([α]1,[β]1[β]2,[γ]2,[δ]1[δ]2,srsG1,srsG2,srs for h(τ)t(τ),[Ψ1]1,[Ψ2]1,…,[Ψm]1)
Prover के चरण
Prover के पास एक witness a है और वह यादृच्छिक (random) scalars r और s उत्पन्न करता है।
इस बिंदु पर, आपके पास Solidity में proof verification कोड को समझने के लिए पर्याप्त ज्ञान है। यहाँ Tornado Cash का proof verification कोड दिया गया है। पाठक को source code को ध्यान से पढ़ने के लिए प्रोत्साहित किया जाता है। यदि पाठक Solidity असेंबली प्रोग्रामिंग के साथ सहज है, तो इस source code को समझना मुश्किल नहीं होगा क्योंकि वेरिएबल नाम इस लेख में दिए गए नामों के अनुरूप हैं।
Groth16 proofs malleable होते हैं। एक वैध (valid) proof
([A]1,[B]2,[C]1) दिए जाने पर, एक हमलावर [A]1 और [B]2 के बिंदु निषेध (point negation) की गणना कर सकता है और ([A′]1,[B′]2,[C]1) के रूप में एक नया proof प्रस्तुत कर सकता है जहाँ [A′]1=neg([A]1) और [B′]2=neg([B]2) है।
यह देखने के लिए कि [A]1∙[B]2=[A′]1∙[B′]2 कैसे होता है, निम्नलिखित कोड पर विचार करें:
इस हमले से बचाव का वर्णन अगले अनुभाग में किया गया है।
आप इस लेख में इस हमले का एक proof of concept देख सकते हैं।
Prover एक ही witness के लिए असीमित संख्या में proofs बना सकता है
यह अपने आप में कोई “सुरक्षा मुद्दा” नहीं है – Zero Knowledge प्राप्त करने के लिए यह आवश्यक है। हालाँकि, एप्लिकेशन को यह ट्रैक करने के लिए एक तंत्र (mechanism) की आवश्यकता होती है कि कौन से तथ्य पहले ही साबित हो चुके हैं और इसे प्राप्त करने के लिए proof की विशिष्टता (uniqueness) पर निर्भर नहीं रहा जा सकता है।
RareSkills के साथ और जानें
इस तरह की सामग्री को निःशुल्क प्रकाशित करने की हमारी क्षमता हमारे छात्रों के निरंतर समर्थन पर निर्भर करती है। हमारे Zero Knowledge Bootcamp, Web3 Bootcamps के लिए साइन अप करने, या RareTalent पर नौकरी पाने पर विचार करें।