Ethereum प्रीकंपाइल्स (precompiles) उन स्मार्ट कॉन्ट्रैक्ट्स की तरह व्यवहार करते हैं जो Ethereum प्रोटोकॉल में निर्मित (built-in) होते हैं। नौ प्रीकंपाइल्स 0x01 से 0x09 एड्रेस में रहते हैं।
प्रीकंपाइल्स की उपयोगिता को चार श्रेणियों में बांटा गया है:
- एलिप्टिक कर्व डिजिटल सिग्नेचर रिकवरी (Elliptic curve digital signature recovery)
- Bitcoin और zcash के साथ इंटरैक्ट करने के लिए हैश मेथड्स
- मेमोरी कॉपी करना
- ज़ीरो नॉलेज प्रूफ़्स (zero knowledge proofs) के लिए एलिप्टिक कर्व मैथ को सक्षम करने के तरीके
इन ऑपरेशन्स को इतना वांछनीय माना गया कि इन्हें करने के लिए गैस-कुशल (gas-efficient) मैकेनिज़्म होना ज़रूरी था। इन एल्गोरिदम को Solidity में लागू करना गैस के मामले में काफी कम कुशल (less gas efficient) होगा।
प्रीकंपाइल्स किसी स्मार्ट कॉन्ट्रैक्ट के अंदर निष्पादित (execute) नहीं होते हैं, वे Ethereum क्लाइंट स्पेसिफिकेशन का हिस्सा हैं। आप Geth Client में उनकी एक सूची यहां देख सकते हैं। क्योंकि वे एक प्रोटोकॉल स्पेसिफिकेशन हैं, वे Ethereum Yellow Paper (परिशिष्ट E में) सूचीबद्ध हैं।
Solidity के साथ Precompiled Smart Contracts को कॉल करना
अधिकांश प्रीकंपाइल्स में Solidity रैपर नहीं होता है (केवल ecRecover इसका एकमात्र अपवाद है)। आपको सीधे addressOfPrecompile.staticcall(...) के साथ एड्रेस को कॉल करना होगा या असेंबली का उपयोग करना होगा।
हालांकि कोई भी प्रीकंपाइल्ड कॉन्ट्रैक्ट स्टेट बदलने (state changing) वाला नहीं है, लेकिन जो Solidity फ़ंक्शन उन्हें कॉल करता है वह pure नहीं हो सकता है क्योंकि Solidity कंपाइलर के पास यह अनुमान लगाने का कोई तरीका नहीं है कि staticcall स्टेट को नहीं बदलेगा।
Address 0x01: ecRecover
ECRecover एक हैश और उस हैश के लिए डिजिटल सिग्नेचर से एड्रेस रिकवर करने के लिए एक प्रीकंपाइल है, यानी यह निर्धारित करना कि यदि सिग्नेचर वैध है तो इसे किसने साइन किया है। (हमारे ट्यूटोरियल में Solidity digital signatures का उपयोग करने के तरीके के बारे में अधिक जानें)।
उदाहरण:
function recoverSignature(bytes32 hash, uint8 v, bytes32 r, bytes32 s) public view returns (address) {
address r = ecrecover(hash, v, r, s);
require(r != address(0), "signature is invalid");
}
सावधान रहें (Beware): जब सिग्नेचर हैश के विरुद्ध मान्य (validate) नहीं होता है, तो ecrecover रिवर्ट (revert) नहीं करता है। यह शून्य एड्रेस (zero address) लौटाता है। आपको हमेशा स्पष्ट रूप से इसकी जांच करनी चाहिए, या इससे भी बेहतर, Openzeppelin लाइब्रेरी का उपयोग करें जो आपके लिए इसे संभालती है। यदि आप नहीं जानते कि आप क्या कर रहे हैं, तो सिग्नेचर के साथ कई चीजें गलत हो सकती हैं!
Address 0x02 और 0x03: SHA-256 और RIPEMD-160
ये दोनों प्रीकंपाइल्स calldata में दिए गए बाइट्स को हैश करेंगे। यहां SHA256 का एक उदाहरण दिया गया है। सरलता के लिए, हम एक uint256 को हैश करेंगे:
function hashSha256(uint256 numberToHash) public view returns (bytes32 h) {
(bool ok, bytes memory out) = address(2).staticcall(abi.encode(numberToHash));
require(ok);
h = abi.decode(out, (bytes32));
}
और यहाँ यह RIPEMD-160 के साथ है:
function hashRIPEMD160(bytes calldata data) public view returns (bytes20 h) {
(bool ok, bytes memory out) = address(3).staticcall(data);
require(ok);
h = bytes20(abi.decode(out, (bytes32)) << 96);
}
हालांकि RIPEMD-160 20 बाइट्स लौटाता है, EVM केवल 32 बाइट की वृद्धि (increments) में काम कर सकता है, यही कारण है कि ऊपर दिए गए उदाहरण कोड में बिटशिफ्टिंग (bitshifting) और कास्टिंग (casting) का उपयोग किया गया है।
Ethereum SHA-256 और RIPEMD-160 का समर्थन क्यों करता है? Bitcoin उसी तरह SHA256 का भारी उपयोग करता है जैसे Ethereum keccak256 का भारी उपयोग करता है। हालांकि, Bitcoin एड्रेस पब्लिक की (public key) को हैश करने और पब्लिक एड्रेस को अधिक कॉम्पैक्ट बनाने के लिए RIPEMD-160 का उपयोग करते हैं। यह इस बात के तुलनीय है कि कैसे Ethereum ECDSA पब्लिक की के keccak256 के अंतिम 20 बाइट्स (160 बिट्स, RIPEMD की तरह) लेता है।
Yul Assembly का उपयोग करना
क्योंकि रिटर्न साइज़ पहले से ज्ञात होता है, इसलिए returndatasize ऑपकोड (opcode) का उपयोग करने की कोई आवश्यकता नहीं है। Yul में (और ऑपकोड में) staticcall छह आर्ग्युमेंट्स लेता है:
- args
- फॉरवर्ड करने के लिए गैस (gas to forward)
- हैश किए जाने वाले डेटा को मेमोरी में कहाँ खोजना है
- हैश किए जाने वाले डेटा का साइज़ (32 बाइट्स)
- आउटपुट कहाँ लिखना है
- आउटपुट का साइज़
नीचे दिए गए कोड में, हम uint256 को मेमोरी में लिखते हैं और फिर इसे हैशिंग के लिए एड्रेस 2 पर पास करते हैं।
function hashSha256Yul(uint256 numberToHash) public view returns (bytes32) {
assembly {
mstore(0, numberToHash) // store number in the zeroth memory word
let ok := staticcall(gas(), 2, 0, 32, 0, 32)
if iszero(ok) {
revert(0,0)
}
return(0, 32)
}
}
Address 0x04: Identity
आइडेंटिटी प्रीकंपाइल (identity precompile) मेमोरी के एक क्षेत्र (region) को दूसरे में कॉपी करता है। Ethereum में ‘‘memcopy’’ ऑपकोड (मेमोरी में एक क्षेत्र को दूसरे में कॉपी करने के लिए एक ऑपकोड) नहीं है। सामान्य तौर पर, आपको स्टैक पर मेमोरी के एक वर्ड को MLOAD करना होगा और फिर इसे कॉपी करने के लिए इसे MSTORE करना होगा, और आपको कॉपी का काम वर्ड-बाय-वर्ड करना होगा। आइडेंटिटी प्रीकंपाइल के साथ, आप एक बार में एक बाइट के बजाय एक ही बार में 32 बाइट वर्ड्स के निरंतर सेट (contiguous set) को कॉपी कर सकते हैं।
Address 0x05: Modexp
ECDSA पब्लिक एन्क्रिप्शन (public encryption) का समर्थन नहीं करता है। यदि किसी एप्लिकेशन में इसके लिए कोई उपयोग का मामला (use case) है, तो पुराने जमाने के अच्छे RSA एन्क्रिप्शन का उपयोग किया जाना चाहिए। उच्च स्तर पर, RSA एक संदेश को लेकर काम करता है, इसे प्राप्तकर्ता की पब्लिक की (public key) की घात (power) और किसी बहुत बड़ी संख्या के मॉड्यूलो (modulo) तक बढ़ाता है। परिणामी संख्या एन्क्रिप्टेड संदेश होती है। चूंकि यह संदेश की लंबाई को गंभीर रूप से सीमित करता है, इसलिए विशिष्ट संदेश एक्सचेंज AES-256 जैसी सिमेट्रिक की (symmetric key) को एन्क्रिप्ट करके और उसे प्राप्तकर्ता को भेजकर काम करता है। फिर प्राप्तकर्ता संदेश को डिक्रिप्ट करने के लिए AES-256 की का उपयोग कर सकता है।
RSA के साथ संदेशों पर हस्ताक्षर (Signing messages) करना विपरीत दिशा में काम करता है। प्रेषक (sender) संदेश के हैश को अपनी प्राइवेट की (private key) की घात और बड़ी संख्या (जो सार्वजनिक रूप से ज्ञात है) के मॉड्यूलो तक बढ़ाता है। इसका परिणाम संदेश का हस्ताक्षर (signature) होता है। प्राप्तकर्ता हस्ताक्षर को पब्लिक की (public key) की घात और बड़ी संख्या के मॉड्यूलो तक बढ़ाकर और यह देखकर कि इसका परिणाम संदेश हैश में आता है, हस्ताक्षर को सत्यापित (verify) कर सकता है।
Ethereum के पास RSA के लिए पब्लिक की इन्फ्रास्ट्रक्चर (public key infrastructure) नहीं है। हालांकि, एक Ethereum एड्रेस अपने Ethereum एड्रेस को RSA साइन करके RSA पब्लिक की (public key) के स्वामित्व को साबित कर सकता है। ध्यान दें कि यह विपरीत दिशा में काम नहीं करता है। RSA पब्लिक की को ECDSA साइन करना सुरक्षित नहीं है क्योंकि कोई भी RSA पब्लिक की सहित किसी भी मनमानी स्ट्रिंग (arbitrary string) को ECDSA साइन कर सकता है।
आप इस विषय पर हमारे दूसरे लेख में RSA with Solidity के लिए एक एप्लिकेशन देख सकते हैं।
यहाँ Solidity में uint256 के साथ modExp का उपयोग करने का एक उदाहरण दिया गया है:
function modExp(uint256 base, uint256 exp, uint256 mod) public view returns (uint256) {
bytes memory precompileData = abi.encode(32, 32, 32, base, exp, mod);
(bool ok, bytes memory data) = address(5).staticcall(precompileData);
require(ok, "expMod failed");
return abi.decode(data, (uint256));
}
Address 0x06 और 0x07 और 0x08: ecAdd, ecMul, और ecPairing (EIP-196 और EIP-197)
इन प्रीकंपाइल्स का उपयोग zero knowledge proof cryptography को अधिक कुशल बनाने के लिए किया जाता है। वास्तव में, आप Tornado Cash ज़ीरो नॉलेज प्रूफ़ वेरिफ़ायर (zero knowledge proof verifier) में इन तीनों प्रीकंपाइल्स का उपयोग होते हुए देख सकते हैं:
एलिप्टिक कर्व एडिशन (Elliptic Curve Addition): staticcall to address(6)
एलिप्टिक कर्व मल्टीप्लिकेशन (Elliptic Curve Multiplication): staticcall to address(7)
एलिप्टिक कर्व पेयरिंग (Elliptic Curve Pairing): static call to address(8)
ये ऑपरेशन्स केवल BN-128 Barreto-Naehrig एलिप्टिक कर्व्स (elliptic curves) का समर्थन करते हैं। ये डिजिटल सिग्नेचर के लिए उपयोग किए जाने वाले एलिप्टिक कर्व्स के समान नहीं हैं।
ecAdd और ecMul को EIP-196 EIP-196 में जोड़ा गया था और ecPairing को EIP-197 में जोड़ा गया था।
आप जान सकते हैं कि ये प्रीकंपाइल्स कैसे काम करते हैं हमारे अन्य ट्यूटोरियल्स में:
Elliptic Curves in Finite Fields
Bilinear Pairings
ecAdd, ecMul, और ecPairing के लिए गैस कॉस्ट (Gas Costs)
EIP-1108 की शुरूआत के साथ इन प्रीकंपाइल्स के लिए गैस कॉस्ट को उनके मूल स्पेसिफिकेशन से कम कर दिया गया था। उपयोगकर्ताओं को उनकी गैस कॉस्ट के बारे में अद्यतित (up-to-date) जानकारी के लिए संबंधित EIP स्पेसिफिकेशन के बजाय उस EIP को देखना चाहिए।
Address 0x09: Blake2 (EIP-152)
Blake2 हैश zcash का पसंदीदा हैश है। SHA256 और RIPEMD-160 के समान, Ethereum को उस ब्लॉकचेन पर लेनदेन के दावों को मान्य (validate) करने में सक्षम बनाने के लिए Blake2 को जोड़ा गया था। इस प्रीकंपाइल को EIP-152 में जोड़ा गया था और प्रस्ताव (proposal) पर कुछ सैंपल कोड उपलब्ध है।
Address 0xa: Point evaluation precompile (EIP-4844)
Decun हार्डफोर्क ने KZG कमिटमेंट्स (commitments) को सत्यापित करने के लिए एड्रेस 10 (address 0xa) पर एक प्रीकंपाइल precompile at address 10 (address 0xa) जोड़ा। अर्थात्, एक ब्लॉब कमिटमेंट (blob commitment) और एक ज़ीरो नॉलेज प्रूफ़ (zero knowledge proof) दिए जाने पर, यदि प्रूफ़ अमान्य है तो प्रीकंपाइल रिवर्ट (revert) कर देता है।
अन्य चेन्स पर Precompiles
स्मार्ट कॉन्ट्रैक्ट डेवलपर्स को अन्य EVM संगत चेन्स (EVM compatible chains) में Solidity कोड कॉपी करते समय सावधान रहना चाहिए क्योंकि उन चेन्स पर प्रीकंपाइल्स Ethereum के प्रीकंपाइल्स से मेल नहीं खा सकते हैं। उदाहरण के लिए, ecrecover और अन्य क्रिप्टोग्राफ़िक प्रीकंपाइल्स zksync पर समर्थित नहीं हैं। (इसके तकनीकी कारण यह हैं कि अधिकांश क्रिप्टोग्राफी एल्गोरिदम SNARK-फ़्रेंडली नहीं हैं, वे ज़ीरो नॉलेज प्रूफ़ के दृष्टिकोण से सत्यापित करने के लिए महंगे हैं)।
और जानें (Learn More)
यह सामग्री हमारे Solidity bootcamp का हिस्सा है। आप हमारे मुफ़्त Solidity course के साथ भी मुफ्त में Solidity सीख सकते हैं।
मूल रूप से 16 अप्रैल, 2023 को प्रकाशित