608060405260405160893803806089833981016040819052601e916025565b600055603d565b600060208284031215603657600080fd5b5051919050565b603f80604a6000396000f3fe6080604052600080fdfea26469706673582212204a131c1478e0e7bb29267fd8f6d38a660b40a25888982bd6618b720d4498b6b464736f6c634300080700330000000000000000000000000000000000000000000000000000000000000001
यह लेख बताता है कि जब कोई Ethereum स्मार्ट कॉन्ट्रैक्ट बनाया जाता है तो बाइटकोड (bytecode) स्तर पर क्या होता है और कंस्ट्रक्टर आर्ग्यूमेंट्स (constructor arguments) को कैसे इंटरप्रेट (interpret) किया जाता है।
विषय सूची
हम विज़ुअल उदाहरणों के साथ निम्नलिखित विषयों पर चर्चा करेंगे:
- परिचय
- Init कोड
- Payable कंस्ट्रक्टर कॉन्ट्रैक्ट
- Non-payable कंस्ट्रक्टर कॉन्ट्रैक्ट
- Runtime कोड
- Runtime कोड ब्रेकडाउन
- पैरामीटर्स के साथ कंस्ट्रक्टर
परिचय
उच्च स्तर पर, कॉन्ट्रैक्ट को डिप्लॉय (deploy) करने वाला वॉलेट नल एड्रेस (null address) पर एक ट्रांजैक्शन भेजता है, जिसमें ट्रांजैक्शन डेटा को तीन भागों में रखा जाता है:
<init code> <runtime code> <constructor parameters>
इन्हें एक साथ क्रिएशन कोड (creation code) कहा जाता है। EVM init कोड को एग्जीक्यूट (execute) करने से शुरुआत करता है। यदि init कोड ठीक से एन्कोड किया गया है, तो यह एग्जीक्यूशन रनटाइम कोड (runtime code) को ब्लॉकचेन पर स्टोर कर देगा।
EVM स्पेसिफिकेशन में ऐसा कुछ भी नहीं है जो यह कहता हो कि लेआउट init कोड, रनटाइम कोड और कंस्ट्रक्टर पैरामीटर्स ही होना चाहिए। यह init कोड, कंस्ट्रक्टर पैरामीटर्स और फिर रनटाइम कोड भी हो सकता है। यह केवल वह परंपरा (convention) है जिसका उपयोग Solidity करता है। हालाँकि, EVM को यह जानने के लिए कि एग्जीक्यूशन कहाँ से शुरू करना है, init कोड पहला भाग होना चाहिए।
पूर्वापेक्षाएँ (Prerequisites)
यह लेख मानकर चलता है कि आपको निम्नलिखित विषयों का ज्ञान है:
- Solidity (यदि आप अभी शुरुआत कर रहे हैं तो हमारा free Solidity tutorial देखें)।
- EVM opcodes की मूल बातें
आइए शुरू करते हैं!
लेखकत्व (Authorship)
यह लेख RareSkills Technical Writing Program के हिस्से के रूप में Michael Amadi (LinkedIn, Twitter) द्वारा सह-लिखा गया था।
Solidity creationCode
Solidity में creationCode कीवर्ड के माध्यम से उस बाइटकोड को प्राप्त करने का एक मैकेनिज्म है जिसे स्मार्ट कॉन्ट्रैक्ट क्रिएशन ट्रांजैक्शन के दौरान डिप्लॉय किया जाएगा। इसे नीचे दर्शाया गया है।
इसमें कंस्ट्रक्टर आर्ग्यूमेंट्स शामिल नहीं होते हैं, जो कॉन्ट्रैक्ट डिप्लॉयमेंट के दौरान चलाए गए बाइटकोड के हिस्से के रूप में शामिल होंगे। init कोड (creationCode) और आर्ग्यूमेंट्स को कैसे स्ट्रक्चर किया जाता है, यह इस लेख में बताया गया है।
contract ValueStorage {
uint256 public value;
constructor(uint256 value_) {
value = value_;
}
}
contract GetCreationCode {
function get() external returns (bytes memory creationCode) {
creationCode = type(Simple).creationCode;
}
}
Init कोड
Init कोड क्रिएशन कोड का वह अंश (fragment) है जो कॉन्ट्रैक्ट को डिप्लॉय करने के लिए जिम्मेदार होता है। आइए सबसे सरल संभव स्मार्ट कॉन्ट्रैक्ट पर नज़र डालें। हम बाद में बताएंगे कि हमने payable कंस्ट्रक्टर क्यों जोड़ा।
Payable कंस्ट्रक्टर कॉन्ट्रैक्ट
pragma solidity 0.8.17;// optimizer: 200 runscontract Minimal {
constructor() payable {
}
}
संकलन (compilation) परिणाम प्राप्त करने के लिए, हम डिप्लॉयमेंट ट्रांजैक्शन को एग्जीक्यूट करने के बाद रीमिक्स (remix) से “input” फ़ील्ड को कॉपी कर सकते हैं।

कॉन्ट्रैक्ट क्रिएशन बाइटकोड निकालें
जब हम हाइलाइट किए गए फ़ील्ड को कॉपी करते हैं, तो हमें मिलता है
0x6080604052603f8060116000396000f3fe6080604052600080fdfea2646970667358221220d03248cf82928931c158551724bebac67e407e6f3f324f930c4cf1c36e16328764736f6c63430008110033
बेशक इसे पढ़ना काफी कठिन है। हालाँकि, हम इसे दो भागों में बाँट सकते हैं।

ऐसा लग सकता है कि हमने बाइटकोड को किसी रैंडम जगह पर विभाजित कर दिया है, लेकिन इसे बाद में अधिक स्पष्ट रूप से समझाया जाएगा।
यदि हम पहले भाग को evm codes में कॉपी और पेस्ट करते हैं, और बाइटकोड को निमोनिक्स (mnemonics) में बदलते हैं, तो हमें निम्नलिखित output मिलता है। टिप्पणियाँ (Comments) जोड़ दी गई हैं।
// allocate free memory pointer
PUSH1 0x80
PUSH1 0x40
MSTORE
// length of the runtime code
PUSH1 0x3f
DUP1
// where the runtime code begins
PUSH1 0x11
PUSH1 0x00// copy the runtime code from calldata into memory
CODECOPY
// runtime code is deployed at this step
PUSH1 0x00
RETURN
INVALID
विज़ुअल में हाइलाइट किए गए कोड के हिस्से, जिसे रनटाइम कोड (runtime code) कहा जाता है, का आकार 63 बाइट्स (हेक्साडेसिमल में 0x3f) है। यह मेमोरी में 17वें इंडेक्स (हेक्साडेसिमल में 0x11) से शुरू होता है। यह स्पष्ट करता है कि ऊपर दिए गए निमोनिक्स ब्रेकडाउन में 0x3f और 0x11 की वैल्यू कहाँ से आई हैं।
उच्च स्तर पर, इस init कोड के भीतर निम्नलिखित तीन क्रियाएं होती हैं:
- फ़्री मेमोरी पॉइंटर (free memory pointer), जो लिखने के लिए अगले उपलब्ध मेमोरी लोकेशन का ट्रैक रखता है, असाइन किया जाता है।
- फिर रनटाइम कोड को
CODECOPYओपकोड (opcode) का उपयोग करके इस मेमोरी लोकेशन में कॉपी किया जाता है। - अंत में, रनटाइम कोड वाले मेमोरी क्षेत्र को EVM को लौटा दिया जाता है जो इसे नए कॉन्ट्रैक्ट के रनटाइम बाइटकोड के रूप में स्टोर करता है।
Non-payable कंस्ट्रक्टर कॉन्ट्रैक्ट
pragma solidity 0.8.17;// optimizer: 200 runs
contract Minimal {
constructor() {
}
}
आइए बाइटकोड पर नज़र डालें जब कंस्ट्रक्टर payable नहीं होता है और अंतर देखें। यह कंपाइलर आउटपुट है।
6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220a6271a05446e269126897aea62fd14e86be796da8d741df53bdefd75ceb4703564736f6c63430008070033
इसे init और रनटाइम कोड में तोड़ने पर, हमारे पास है

आइए payable और non-payable init कोड को अगल-बगल (side by side) रखें।
0x6080604052603f8060116000396000f3fe // payable
0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe // non-payable
हम देख सकते हैं कि payable कॉन्ट्रैक्ट का init कोड non-payable वाले की तुलना में छोटा है। हम नीचे बताते हैं कि ऐसा क्यों है।
लंबे अनुक्रम (non-payable) को evm codes में पेस्ट करने पर, हमें निम्नलिखित output मिलता है, जिसमें टिप्पणियाँ (comments) जोड़ी गई हैं।
// initialize free memory pointer
PUSH1 0x80
PUSH1 0x40
MSTORE
// check the amount of wei that was sent
CALLVALUE
DUP1
ISZERO
// Jump to 0x0f (contract deployment step)
PUSH1 0x0f
JUMPI
// revert if wei sent is greater than 0
PUSH1 0x00
DUP1
REVERT
// Jump dest (0x0f)
JUMPDEST
POP
// length of the runtime code
PUSH1 0x3f
DUP1
// where the runtime code begins
PUSH1 0x1d
PUSH1 0x00
CODECOPY
PUSH1 0x00
RETURN
INVALID
ऊपर क्या हो रहा है यह समझाने के लिए हम इन कॉन्सेप्ट्स (concepts) की व्याख्या और उपयोग करते हैं:
Payable और non-payable कंस्ट्रक्टर्स के बीच अंतर
1. यदि callvalue > 0 है तो init कोड रिवर्ट (revert) हो जाता है, अन्यथा कोड अपना एग्जीक्यूशन जारी रखेगा।
Non-payable कंस्ट्रक्टर में फ़्री मेमोरी पॉइंटर इनिशियलाइज़ेशन और रनटाइम कोड लौटाने के बीच 348015600f57 600080fd 5b50 (12 बाइट्स) का एक अतिरिक्त बाइट अनुक्रम (byte sequence) होता है।
<init bytecode> <extra 12 byte sequence (payable case)> <return runtime bytecode> <runtime bytecode>
यह अतिरिक्त कोड जांचता है कि डिप्लॉयमेंट के दौरान कोई वैल्यू (wei) नहीं भेजी गई है (अनुक्रम 348015600f57) और अन्यथा रिवर्ट हो जाता है (अनुक्रम 600080fd)। अंतिम दो बाइट्स 5b50, JUMPDEST और POP ओपकोड्स हैं जो पहले वर्णित डिप्लॉयमेंट के अनुक्रम को शुरू करते हैं यदि कोई wei नहीं भेजा गया था।
(यहाँ POP होने का कारण यह है कि callvalue अभी भी स्टैक पर है और अब हमें इसकी आवश्यकता नहीं है। JUMPDEST केवल JUMPs और JUMPIs के लिए एक लक्ष्य (target) है। निर्दिष्ट जंप लोकेशन पर इसके बिना JUMPs लैंड नहीं कर सकते और रिवर्ट हो जाएंगे।)
2. रनटाइम कोड के लिए मेमोरी ऑफसेट्स शिफ्ट हो जाते हैं
यह भी ध्यान दें कि रनटाइम कोड की लंबाई नहीं बदलती है लेकिन रनटाइम कोड को कॉपी करने का ऑफसेट बदल जाता है क्योंकि init कोड लंबा होता है जो रनटाइम कोड के ऑफसेट को और नीचे शिफ्ट कर देता है।
Init बाइटकोड के लिए non-payable ऑफसेट 0x1d है, और payable मामले के लिए ऑफसेट 0x11 पर छोटा है। यदि हम उन्हें घटाते हैं (0x1d - 0x11 = 0x0c, दशमलव में 12) तो हमें फ़्री मेमोरी पॉइंटर इनिशियलाइज़ेशन चंक और उस अनुक्रम के बीच नॉन-ज़ीरो वैल्यू की जाँच करने वाले अतिरिक्त बाइट अनुक्रम का आकार मिलता है जहाँ रनटाइम बाइटकोड लौटाया जाता है।
खाली (empty) कॉन्ट्रैक्ट के लिए रनटाइम कोड
कंपाइलर द्वारा जोड़े गए मेटाडेटा (metadata) के कारण एक खाली कॉन्ट्रैक्ट में रनटाइम कोड खाली (non-empty) नहीं होता है
रनटाइम कोड क्रिएशन कोड का वह अंश है जो init कोड द्वारा लौटाया जाता है और यह उस कॉन्ट्रैक्ट का बाइटकोड बनने के लिए सेट होता है जिसे डिप्लॉयमेंट के बाद यूज़र्स द्वारा कॉल किया जा सकता है। यह वह बन जाता है जिसे हम “स्मार्ट कॉन्ट्रैक्ट” के रूप में जानते हैं।
एक सवाल उठता है, “यदि कॉन्ट्रैक्ट खाली है (कोई फ़ंक्शन नहीं है), तो रनटाइम कोड खाली क्यों नहीं है?”
Solidity कंपाइलर रनटाइम कोड में आपके कॉन्ट्रैक्ट के बारे में कुछ मेटाडेटा जोड़ता है। कॉन्ट्रैक्ट मेटाडेटा के बारे में अधिक जानकारी यहाँ प्राप्त करें। ओपकोड fe INVALID, मेटाडेटा को एग्जीक्यूट होने से रोकने के लिए इसके पहले (prepend) लगाया जाता है।
(नया Solidity वर्ज़न 0.8.18 एक कंपाइलर सेटिंग --no-cbor-metadata जोड़ता है जहाँ आप कंपाइलर को बता सकते हैं कि इस मेटाडेटा को आपके कॉन्ट्रैक्ट के बाइटकोड में न जोड़े)
प्योर (pure) Yul कॉन्ट्रैक्ट में कंपाइलर डिफ़ॉल्ट रूप से मेटाडेटा नहीं जोड़ता है
यदि कॉन्ट्रैक्ट को प्योर Yul में लिखा गया होता, तो कोई मेटाडेटा नहीं होता। हालाँकि, ग्लोबल ऑब्जेक्ट में .metadata शामिल करके, मेटाडेटा सेक्शन को जोड़ा जा सकता है।
// the output of the compilation of this contract// will have no metadata by default
object "Simple" {
code {
datacopy(0, dataoffset("runtime"), datasize("runtime"))
return(0, datasize("runtime"))
}
object "runtime" {
code {
mstore(0x00, 2)
return(0x00, 0x20)
}
}
}
कंपाइलर output इस प्रकार है 6000600d60003960006000f3fe और जब इसे निमोनिक्स (mnemonics) में बदला जाता है, तो हमें मिलता है
// copy runtime code to memory
PUSH1 00
PUSH1 0d
PUSH1 00
CODECOPY
// Returning a zero sized region because there is no runtime code
PUSH1 00
PUSH1 00
RETURN
INVALID
इस मामले में, लौटाई गई मेमोरी का क्षेत्र शून्य है, क्योंकि कोई रनटाइम कोड या मेटाडेटा नहीं है।
(कंपाइलर 0x0d से शुरू होता है और ऑफसेट 0x00 से शुरू होने वाली मेमोरी में रनटाइम कोड के 0x00 बाइट्स कॉपी करता है। फिर 0x00 बाइट्स लौटाता है।)
एक गैर-रिक्त (non-empty) कॉन्ट्रैक्ट के लिए रनटाइम कोड
अब चलिए कॉन्ट्रैक्ट में सबसे सरल संभव लॉजिक जोड़ते हैं।
pragma solidity 0.8.7;contract Runtime {
address lastSender;
constructor () payable {}
receive() external payable {
lastSender = msg.sender;
}
}
आउटपुट क्रिएशन कोड है
608060405260578060116000396000f3fe608060405236601c57600080546001600160a01b03191633179055005b600080fdfea2646970667358221220e9b731ab28726d97cbf5219f1e5eaec508f23254c60b15ed1d3456572547c5bf64736f6c63430008070033.
इसे इस प्रकार अलग किया जा सकता है

आइए रनटाइम कोड को विस्तार से देखें
चूँकि यह एक Solidity कॉन्ट्रैक्ट है, इसलिए हम इसे पहले समझाए गए तरीके से एग्जीक्यूटेबल (executable) बाइटकोड और कॉन्ट्रैक्ट मेटाडेटा में विभाजित कर सकते हैं
Runtime code := 0x608060405236601c57600080546001600160a01b03191633179055005b600080fdfe
Metadata := 0xa2646970667358221220e9b731ab28726d97cbf5219f1e5eaec508f23254c60b15ed1d3456572547c5bf64736f6c63430008070033a2646970667358221220e9b731ab28726d97cbf5219f1e5eaec508f23254c60b15ed1d3456572547c5bf64736f6c63430008070033.
आइए जानें कि evm codes output का उपयोग करके रनटाइम कोड क्या करता है। इसे सरल बनाने के लिए इसे विभाजित किया गया है।
सबसे पहले हम फ़्री मेमोरी पॉइंटर को इनिशियलाइज़ करते हैं।
[00] PUSH1 80
[02] PUSH1 40
[04] MSTORE
यहाँ हम जाँचते हैं कि क्या ट्रांजैक्शन के साथ डेटा भेजा गया था, यदि हाँ तो हम प्रोग्राम काउंटर (PC) 0x1c पर JUMP करते हैं जहाँ हम रिवर्ट करते हैं। किसी कॉन्ट्रैक्ट के लिए डेटा प्राप्त करने के केवल दो मान्य (valid) तरीके रेगुलर फ़ंक्शंस और फ़ॉलबैक (fallback) हैं। हमारे पास केवल एक receive फ़ंक्शन है, इसलिए कॉन्ट्रैक्ट के लिए calldata प्राप्त करने का कोई मान्य तरीका नहीं है।
[05] CALLDATASIZE
[06] PUSH1 1c
[08] JUMPI
और फिर हमारे पास वह कोड है जो msg.sender को स्टोर करता है।
[09] PUSH1 00
[0b] DUP1
[0c] SLOAD
[0d] PUSH1 01
[0f] PUSH1 01
[11] PUSH1 a0
[13] SHL
[14] SUB
[15] NOT
[16] AND
[17] CALLER
[18] OR
[19] SWAP1
[1a] SSTORE
[1b] STOP
यह उस स्थिति के लिए JUMPDEST 0x1c है जहाँ calldata भेजा गया था। ट्रांजैक्शन रिवर्ट हो जाता है।
[1c] JUMPDEST
[1d] PUSH1 00
[1f] DUP1
[20] REVERT
[21] INVALID
पैरामीटर्स के साथ कंस्ट्रक्टर
कंस्ट्रक्टर आर्ग्यूमेंट्स वाले कॉन्ट्रैक्ट्स को थोड़ा अलग तरीके से एन्कोड किया जाता है। कंस्ट्रक्टर पैरामीटर्स को क्रिएशन कोड के अंत में (रनटाइम कोड के बाद) जोड़े जाने की उम्मीद होती है और वे ABI एन्कोडेड होते हैं।
विशेष रूप से Solidity यह सुनिश्चित करने के लिए एक अतिरिक्त चेक (check) जोड़ता है कि कंस्ट्रक्टर पैरामीटर की लंबाई कम से कम अपेक्षित कंस्ट्रक्टर आर्ग्यूमेंट्स की लंबाई के बराबर हो, अन्यथा यह रिवर्ट हो जाता है।
आइए एक सरल उदाहरण देखें। सरलता के लिए हम कोई रनटाइम कोड शामिल नहीं करते हैं। हम जो एकमात्र कोड शामिल करते हैं वह कंस्ट्रक्टर में है, जो रनटाइम कोड का हिस्सा नहीं है।
// optimizer: 200contract MinimalLogic {
uint256 private x;
constructor (uint256 _x) payable {
x =_x;
}
}
क्रिएशन कोड है
608060405260405160893803806089833981016040819052601e916025565b600055603d565b600060208284031215603657600080fd5b5051919050565b603f80604a6000396000f3fe6080604052600080fdfea26469706673582212204a131c1478e0e7bb29267fd8f6d38a660b40a25888982bd6618b720d4498b6b464736f6c63430008070033
इसे तोड़ने पर हमें मिलता है
"Init code": 0x608060405260405160893803806089833981016040819052601e916025565b600055603d565b600060208284031215603657600080fd5b5051919050565b603f80604a6000396000f3fe"Runtime code (metadata only)": 0x6080604052600080fdfea26469706673582212204a131c1478e0e7bb29267fd8f6d38a660b40a25888982bd6618b720d4498b6b464736f6c63430008070033"Constructor arguments are missing!"
इस तरह क्रिएशन कोड को एग्जीक्यूट करने से init कोड में रिवर्ट हो जाएगा क्योंकि यह uint256 _x के रूप में उपयोग करने के लिए रनटाइम कोड के बाद कम से कम 32 बाइट्स की अपेक्षा करता है। हम प्रत्येक ओपकोड को तोड़ते समय इसे अधिक विस्तार से देखेंगे। अभी के लिए हम _x के रूप में उपयोग करने के लिए क्रिएशन कोड को ABI एन्कोडेड uint256(1) के रूप में जोड़ (append) सकते हैं।
अब, सही किया गया बाइटकोड
608060405260405160893803806089833981016040819052601e916025565b600055603d565b600060208284031215603657600080fd5b5051919050565b603f80604a6000396000f3fe6080604052600080fdfea26469706673582212204a131c1478e0e7bb29267fd8f6d38a660b40a25888982bd6618b720d4498b6b464736f6c634300080700330000000000000000000000000000000000000000000000000000000000000001
आइए evm codes output का उपयोग करके इसका विश्लेषण करें।
चरण 1: फ़्री मेमोरी पॉइंटर को इनिशियलाइज़ करें
Solidity कॉन्ट्रैक्ट्स के साथ हमेशा की तरह, हम फ़्री मेमोरी पॉइंटर को 6080604052 के साथ इनिशियलाइज़ करते हैं।
चरण 2: कंस्ट्रक्टर पैरामीटर की लंबाई प्राप्त करें
// 6040 51 6089 38 03
PC OPCODE
[05] PUSH1 40
[07] MLOAD
[08] PUSH1 89
[0a] CODESIZE
[0b] SUB
यहाँ PUSH1 40 MLOAD बाद में उपयोग करने के लिए फ़्री मेमोरी पॉइंटर को MLOAD करता है। हम क्रिएशन कोड की लंबाई (कंस्ट्रक्टर पैरामीटर्स के बिना) को PUSH1 89 के साथ पुश करते हैं और फिर CODESIZE को कॉल करते हैं (इसमें कंस्ट्रक्टर पैरामीटर शामिल होता है)। हम कंस्ट्रक्टर पैरामीटर्स की लंबाई प्राप्त करने के लिए दोनों को घटाते हैं।
चरण 3: कंस्ट्रक्टर पैरामीटर को मेमोरी में कॉपी करें
// 80 6089 83 39
PC OPCODE
[0c] DUP1
[0d] PUSH1 89
[0f] DUP4
[10] CODECOPY
यहाँ हम CODECOPY के लिए स्टैक (stack) तैयार करते हैं। हम ओपकोड DUP1 के साथ ऊपर से घटाव के परिणाम (subtraction result) को डुप्लिकेट (duplicate) करते हैं और PUSH1 89 के साथ स्टैक पर 0x89 (कंस्ट्रक्टर आर्ग्यूमेंट्स के बिना क्रिएशन कोड की लंबाई) को पुश करते हैं। अंत में हम मेमोरी ऑफसेट को स्टैक के शीर्ष पर लाने के लिए DUP4 का उपयोग करते हैं। अब हम कंस्ट्रक्टर पैरामीटर को फ़्री मेमोरी पॉइंटर पर मेमोरी में कॉपी करने के लिए CODECOPY को कॉल करते हैं।
चरण 4: फ़्री मेमोरी पॉइंटर को अपडेट करें
कोड को मेमोरी में लिखने के बाद, Solidity फ़्री मेमोरी पॉइंटर को निम्नानुसार अपडेट करता है।
// 81 01 6040 81 90 52
PC OPCODE
[11] DUP2
[12] ADD
[13] PUSH1 40
[15] DUP2
[16] SWAP1
[17] MSTORE
हम यहाँ कंस्ट्रक्टर पैरामीटर की लंबाई (0x20), जिसे हमने पहले डुप्लिकेट किया था, को फ़्री मेमोरी पॉइंटर (0x80) में जोड़कर और फिर MSTORE 40 को कॉल करने से पहले DUP1 और SWAP1 ऑपरेशन्स के साथ व्यवस्थित (arrange) करके ऐसा करते हैं, जो नई वैल्यू (0xa0) को फ़्री मेमोरी पॉइंटर के रूप में स्टोर करता है।
इसके बाद हमारे पास डायनामिक (dynamic) ऑपरेशन्स और JUMPs की एक श्रृंखला है जो क्रमिक रूप से (sequentially) एग्जीक्यूट नहीं होते हैं बल्कि कुछ शर्तों पर आधारित होते हैं। आइए गहराई से समझें।
चरणों को क्रमांकित (numbered) किया गया है ताकि आप आवश्यक JUMPDEST की तलाश किए बिना उनका क्रमिक रूप से पालन कर सकें।
आप इस बाइटकोड को स्वयं आज़माने के लिए इसके playground link का भी उपयोग कर सकते हैं।
चरण 5: SSTORE के JUMPDEST पर जंप (Jump) करें
// 601e 91 6025 56
PC OPCODE
[18] PUSH1 1e
[1a] SWAP2
[1b] PUSH1 25
[1d] JUMP // jump to JUMPDEST 0x25
हम उस PC पर जंप करना चाहते हैं जो कंस्ट्रक्टर पैरामीटर को स्टोरेज (storage) में स्टोर करने के लिए ऑपरेशन्स करता है।
हम स्टैक पर 1e पुश करते हैं। 1e प्रोग्राम काउंटर में वह स्थिति है जहाँ SSTORE वास्तव में एग्जीक्यूट होता है, लेकिन पहले हमें यह जाँचना होगा कि कॉपी किया गया कंस्ट्रक्टर पैरामीटर कम से कम 32 बाइट्स का हो। यह ऑपरेशन प्रोग्राम काउंटर 0x25 पर शुरू होता है, जो ऊपर दिया गया JUMPDEST है।
चरण 8: कंस्ट्रक्टर आर्ग्यूमेंट को स्टोरेज स्लॉट 0 में स्टोर करता है
यह JUMPDEST 0x1e है, JUMPDEST 0x25 पहले एग्जीक्यूट होता है और नीचे दिया गया है। ध्यान दें कि यह चरण 8 है, और पिछला अनुभाग चरण 5 था। यह केवल तभी एग्जीक्यूट होता है जब 6 और 7 में शर्तें सफलतापूर्वक पूरी हो जाती हैं। हम इसे यहां संकलित (compiled) बाइटकोड के समान अनुक्रम को बनाए रखने के लिए क्रम से बाहर (out of order) प्रस्तुत कर रहे हैं।
// 5b 6000 55 603d 56
PC OPCODE
[1e] JUMPDEST
[1f] PUSH1 00
[21] SSTORE
[22] PUSH1 3d
[24] JUMP
यहाँ हम 0x00 पुश करते हैं जो वह स्टोरेज स्लॉट है जिस पर हम _x को स्टोर करने जा रहे हैं और SSTORE को कॉल करते हैं। फिर अंतिम CODECOPY और RETURN के लिए जंप डेस्टिनेशन को पुश करते हैं।
चरण 6: जाँचता है कि क्या कंस्ट्रक्टर पैरामीटर का आकार कम से कम 32 बाइट्स है
यह JUMPDEST 0x25 है
// 5b 6000 6020 82 84
PC OPCODE
[25] JUMPDEST
[26] PUSH1 00
[28] PUSH1 20
[2a] DUP3
[2b] DUP5
// continue// 03 12 15 6036 57
[2c] SUB
[2d] SLT
[2e] ISZERO
[2f] PUSH1 36
[31] JUMPI // Jump to 0x36 if ISZERO returns 1// else continue and revert// 6000 80 fd
[32] PUSH1 00
[34] DUP1
[35] REVERT
यहाँ हम जाँचते हैं कि कंस्ट्रक्टर पैरामीटर कम से कम 32 बाइट्स का है।
सबसे पहले, हम स्टैक पर 0x00 पुश करते हैं (बाद के लिए), स्टैक पर न्यूनतम स्वीकार्य लंबाई 0x20 (32 बाइट्स) पुश करते हैं। इसके बाद हम इसके ऑफसेट और वर्तमान फ़्री मेमोरी पॉइंटर, जिसे हमने पहले स्टैक पर पुश किया था, की जाँच करके तुलना किए जाने वाले कंस्ट्रक्टर पैरामीटर की लंबाई प्राप्त कर सकते हैं। इसलिए हम स्टैक पर ऑफसेट प्राप्त करने के लिए DUP3 का उपयोग करते हैं और फिर वर्तमान फ़्री मेमोरी पॉइंटर को स्टैक के शीर्ष पर लाने के लिए DUP5 का उपयोग करते हैं।
SUB को कॉल करने से घट जाता है और लंबाई स्टैक पर पुश हो जाती है। अब हम सीधे SLT (signed less than) को कॉल करके यह जाँच सकते हैं कि क्या यह 32 बाइट्स तक है और यदि गलत (false) है तो 0 और यदि सही (true) है तो 1 पुश कर सकते हैं, ISZERO ओपकोड यह जाँचता है कि क्या स्टैक का शीर्ष (SLT परिणाम) 0 है, इसे पॉप (pop) करता है और बूलियन परिणाम को स्टैक पर पुश करता है, हम स्टैक पर अगले JUMP लोकेशन को पुश करते हैं और यदि ISZERO ने 1 लौटाया है तो उस पर जंप करते हैं, अन्यथा हम अमान्य calldata के साथ एग्जीक्यूट होने से बचने के लिए रिवर्ट करते हैं।
चरण 7: पैरामीटर को स्टैक पर लोड करता है और कंस्ट्रक्टर पैरामीटर को स्टोरेज में स्टोर करने के लिए स्टैक को व्यवस्थित करता है
यह JUMPDEST 0x36 है
// 5b 50 51 91 90 50 56
PC OPCODE
[36] JUMPDEST
[37] POP
[38] MLOAD
[39] SWAP2
[3a] SWAP1
[3b] POP
[3c] JUMP // jump to 0x1e
यहाँ हम 0 (चरण 6 से प्रोग्राम काउंटर 26) को पॉप ऑफ (pop off) करते हैं क्योंकि अब हमें इसकी आवश्यकता नहीं है। हम कंस्ट्रक्टर पैरामीटर को स्टैक पर MLOAD करते हैं और कंस्ट्रक्टर पैरामीटर्स के मेमोरी ऑफसेट को साफ़ (clear out) करते हैं क्योंकि अब इसकी भी आवश्यकता नहीं है।
चरण 9: रनटाइम कोड को मेमोरी में कॉपी करता है और उसे लौटाता है
यह JUMPDEST 0x3d है, JUMPDEST 0x1e पहले ऊपर एग्जीक्यूट होता है
// 5b 603f 80 604a 6000 39 6000 f3 fe
PC OPCODE
[3d] JUMPDEST
[3e] PUSH1 3f
[40] DUP1
[41] PUSH1 4a
[43] PUSH1 00
[45] CODECOPY
[46] PUSH1 00
[48] RETURN
[49] INVALID
// Unexecutable code (contract metadata)0x6080604052600080fdfea26469706673582212204a131c1478e0e7bb29267fd8f6d38a660b40a25888982bd6618b720d4498b6b464736f6c63430008070033
यहाँ हम कॉन्ट्रैक्ट के रनटाइम कोड को सामान्य रूप से मेमोरी से लौटाते हैं।
RETURN के एग्जीक्यूट होने से ठीक पहले मेमोरी
0x00 से 0x40 तक (खाली) रनटाइम कोड और मेटाडेटा बाइटकोड है। 0x40 में फ़्री मेमोरी पॉइंटर है। 0x80 में कंस्ट्रक्टर आर्ग्यूमेंट, uint256(1) है।
0x00<->0x20 = 0x6080604052600080fdfea26469706673582212208f9ffa7a3ab43f0ff61d30330x20<->0x40 = 0x624bf0e9d398f9a91213656b13d9ffc8fd90fdbc64736f6c63430008070033000x40<->0x60 = 0x00000000000000000000000000000000000000000000000000000000000000a00x60<->0x80 = 0x00000000000000000000000000000000000000000000000000000000000000000x80<->0xa0 = 0x0000000000000000000000000000000000000000000000000000000000000001
निष्कर्ष (Conclusion)
स्मार्ट कॉन्ट्रैक्ट डिप्लॉयमेंट में कुछ लो-लेवल (low-level) ऑपरेशन्स शामिल होते हैं जिन्हें अधिकांश भाषाओं द्वारा एब्सट्रैक्ट (abstract) कर दिया जाता है। हमने सीखा कि नल एड्रेस पर भेजे गए क्रिएशन कोड का उपयोग करके स्मार्ट कॉन्ट्रैक्ट्स को कैसे एग्जीक्यूट किया जाता है, इस क्रिएशन कोड के विभिन्न भाग क्या हैं, कॉन्ट्रैक्ट को डिप्लॉय करते समय उनकी क्या भूमिकाएँ हैं और वे एक साथ कैसे काम करते हैं। हमने देखा कि कंस्ट्रक्टर आर्ग्यूमेंट्स को कैसे स्टोर, वैलिडेट (validate), और कॉन्ट्रैक्ट सेट अप करने के लिए उपयोग किया जाता है।
RareSkills Blockchain Bootcamp
हमारे द्वारा दी जाने वाली एक्सपर्ट-लेवल डेवलपर ट्रेनिंग के बारे में अधिक जानने के लिए कृपया हमारी एडवांस blockchain bootcamp पेशकशों को देखें।
मूल रूप से 6 फरवरी, 2023 को प्रकाशित