تشرح هذه المقالة ما يحدث على مستوى الـbytecode عند إنشاء عقد ذكي لإيثريوم وكيفية تفسير وسيطات المنشئ (constructor arguments).
مقدمة
على مستوى عالٍ، ترسل المحفظة التي تنشر العقد معاملة إلى العنوان الفارغ مع بيانات المعاملة الموضحة في ثلاثة أجزاء:
<init code> <runtime code> <constructor parameters>
Bashيُطلق عليهم معًا كود الإنشاء. يبدأ EVM بتنفيذ كود init. إذا تم ترميز كود init بشكل صحيح، فسيقوم هذا التنفيذ بتخزين كود runtime على blockchain.
لا يوجد شيء في مواصفات EVM ينص على أن التخطيط يجب أن يكون كود init وكود runtime ومعلمات المُنشئ (constructor parameters). يمكن أن يكون كود init، ومعلمات المُنشئ (constructor parameters)، ثم كود runtime. هذه ببساطة هو المتعارف عليه في Solidity. ومع ذلك، يجب أن يكون كود init هو الجزء الأول لكي يعرف جهاز EVM مكان بدء التنفيذ.
المتطلبات الأساسية
تفترض هذه المقالة معرفة المواضيع التالية:
- Solidity
- أساسيات EVM opcodes (المكان الجيد للبدء هو evm codes)
دعونا نتعمق!
كود إنشاء Solidity
لدى Solidity آلية للحصول على الـbytecode الذي سيتم نشره أثناء معاملة إنشاء العقد الذكي عبر createCode. وهذا موضح أدناه.
لا يتضمن هذا وسيطات المُنشئ (constructor arguments)، والتي سيتم تضمينها كجزء من الـbytecode الذي يتم تشغيله أثناء نشر العقد. يتم شرح كيفية تنظيم كود 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;
}
}
Solidityكود Init
كود init هو جزء من كود الإنشاء المسؤول عن نشر العقد. دعونا نلقي نظرة على أبسط عقد ذكي ممكن. سنشرح سبب إضافة مُنشئ مستحق الدفع (payable constructor) لاحقًا.
عقد مع مُنشئ مستحق الدفع
pragma solidity 0.8.17;// optimizer: 200 runscontract Minimal {
constructor() payable {
}
}
Solidityللحصول على نتيجة التجميع، يمكننا نسخ حقل “input” من الريمكس بعد تنفيذ معاملة النشر.
عندما ننسخ الحقل الموضح نحصل على
0x6080604052603f8060116000396000f3fe6080604052600080fdfea2646970667358221220d03248cf82928931c158551724bebac67e407e6f3f324f930 c4cf1c36e16328764736f6c63430008110033
وهذا بالطبع صعب القراءة إلى حد ما. ومع ذلك، يمكننا تقسيمها إلى قسمين.
قد يبدو أننا قمنا بتقسيم الـbytecode في مكان عشوائي، ولكن سيتم شرح ذلك بشكل أكثر وضوحًا لاحقًا.
إذا قمنا بنسخ الجزء الأول ولصقه في evm codes ، وقمنا بتحويل الـbytecode إلى mnemonics ، فسنحصل على الإخراج التالي. تمت إضافة التعليقات.
// 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
ASMيبلغ حجم قسم كود runtime الموضح في الصورة أعلاه 63 بايت (0x3f بالنظام الست عشري). يبدأ عند الفهرس السابع عشر (0x11 بالنظام الست عشري) في الذاكرة. وهذا ما يفسر مصدر قيم 0x3f و0x11 في تفاصيل الـ mnemonics أعلاه.
على مستوى عالٍ، تحدث الإجراءات الثلاثة التالية ضمن كود init هذا:
- يتم تعيين مؤشر الذاكرة الحرة، الذي يتتبع موقع الذاكرة التالي المتاح للكتابة.
- يتم بعد ذلك نسخ كود runtime إلى موقع الذاكرة هذا باستخدام الـopcode المسمى “CODECOPY”.
- وأخيرًا، يتم إرجاع منطقة الذاكرة التي تحتوي على كود runtime إلى EVM الذي يخزنها كـ runtime bytecode للعقد الجديد.
عقد بدون مُنشئ مستحق الدفع
pragma solidity 0.8.17;// optimizer: 200 runs
contract Minimal {
constructor() {
}
}
Solidityدعونا نلقي نظرة على الـbytecode عندما لا يكون المنشئ مستحق الدفع ونرى الاختلافات. هذا هو إخراج الـ compiler .
6080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea2646970667358221220a6271a05446e269126897aea62 fd14e86be796da8d741df53bdefd75ceb4703564736f6c63430008070033
قمنا بتقسيم هذا إلى أكواد init وruntime
لنضع كود init مستحق الدفع وغير مستحق الدفع جنبًا إلى جنب.
0x6080604052603f8060116000396000f3fe // payable
0x6080604052348015600f57600080fd5b50603f80601d6000396000f3fe // nonpayable
Solidityيمكننا أن نلاحظ أن كود init الخاص بالعقد المستحق الدفع أصغر من كود العقد غير المستحق الدفع. نوضح السبب أدناه.
إذا قمنا بنسخ الجزء الأول ولصقه في evm codes ، وقمنا بتحويل الـbytecode إلى mnemonics ، فسنحصل على الإخراج التالي. تمت إضافة التعليقات.
// 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
ASMلشرح ما يحدث أعلاه ، نشرح ونستخدم هذه المفاهيم:
الفرق بين مُنشئ مستحق الدفع و غير مستحق الدفع
1. يعود كود init إذا كانت قيمة الاستدعاء > 0، وإلا فسيستمر الكود في تنفيذه.
يحتوي المنشئ غير مستحق الدفع على تسلسل بايت إضافي قدره 348015600f57 600080fd 5b50 يساوي (12 بايت) بين تهيئة مؤشر الذاكرة الحرة وإرجاع كود runtime في العقد غير مستحق الدفع.
<init bytecode> <extra 12 byte sequence (payable case)> <return runtime bytecode> <runtime bytecode>
Solidityيتحقق هذا الكود الإضافي من عدم إرسال أي قيمة (wei) أثناء النشر (التسلسل 348015600f57) ويتم إرجاعه بخلاف ذلك (التسلسل 600080fd). البايتتان الأخيرتان 5b50 هما JUMPDEST وPOP الأكواد التي تبدأ تسلسل النشر الموصوف سابقًا في حالة عدم إرسال أي (wei).
(سبب وجود POP هو أن قيمة الاستدعاء لا تزال موجودة في المكدس ولم نعد بحاجة إليها. JUMPDEST هو ببساطة هدف لـ JUMPs وJUMPIs. بدونه في موقع JUMP محدد، لا يمكن للـ JUMPs أن تهبط وسيحدث خطأ .)
2. كود runtime الخاص بـ memory offsets تم تحريكه
ولاحظ أيضًا أن طول كود runtime لا يتغير ولكن الإزاحة لنسخ كود runtime تتغير لأن كود init أطول مما يؤدي إلى إزاحة كود runtime لأسفل.
في حالة أن العقد غير مستحق للدفع تكون الإزاحة او (offset) بالنسبة للـ init bytecod هي 0x1d بينما في حالة أن العقد مستحق للدفع تكون أصغر عند 0x11 إذا قمنا بطرحها (0x1d – 0x11 = 0x0c ، يساوي 12 بالنظام العشري) فسنحصل على حجم تسلسل البايت الإضافي للتحقق من القيمة غير الصفرية الموجود بين تهيئة مؤشر الذاكرة الحرة والتسلسل حيث يتم إرجاع كود بايت runtime
كود runtime لعقد فارغ
كود runtime ليس فارغًا في عقد فارغ بسبب البيانات الوصفية (metadata ) التي يضيفها الـcompiler
كود runtime هو جزء من كود الإنشاء الذي يتم إرجاعه بواسطة كود init ويتم تعيينه ليكون الـbytecode للعقد الذي يمكن للمستخدمين الاتصال به بعد النشر. ويصبح ما نعرفه باسم “العقد الذكي”.
السؤال الذي يطرح نفسه، “إذا كان العقد فارغًا (ليس له وظائف)، فلماذا يكون كود runtime غير فارغ؟”
يقوم solidity compile بإلحاق بعض البيانات الوصفية حول عقدك بكود runtime. مزيد من المعلومات حول البيانات الوصفية للعقد هنا. في الـ opcode يتم إلحاق fe INVALID إلى البيانات الوصفية لمنع تنفيذها.
(يضيف الإصدار الجديد من Solidity 0.8.18 إعداد للـ compiler و هو –no-cbor-metadata حيث يمكنك إخبار المترجم بعدم إلحاق هذه البيانات الوصفية بالـbytecode الخاص بعقدك)
في عقد Yul الخالص، لا يقوم الـ compiler بإضافة البيانات الوصفية بشكل افتراضي
إذا تم كتابة العقد بلغة 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)
}
}
}
Solidityإخرج الـ compiler هو كما يلي 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
ASMفي هذه الحالة، تكون المساحة في الذاكرة التي تم إرجاعها صفرًا، لأنه لا يوجد كود وقت تشغيل أو بيانات وصفيه.
(يبدأ الـ compiler عند 0x0d وينسخ 0x00 بايت من كود runtime إلى الذاكرة بدءًا من الإزاحة 0x00. ثم يقوم بإرجاع 0x00 بايت.)
كود runtime لعقد غير فارغ
الآن دعونا نضيف أبسط منطق ممكن إلى العقد.
pragma solidity 0.8.7;contract Runtime {
address lastSender;
constructor () payable {}
receive() external payable {
lastSender = msg.sender;
}
}
Solidityمُخرج كود إنشاء هو
608060405260578060116000396000f3fe608060405236601c57600080546001600160a01b03191633179055005b600080fdfea2646970667358221220e 9b731ab28726d97cbf5219f1e5eaec508f23254c60b15ed1d3456572547c5bf64736f6c63430008070033.
ويمكن فصل هذا كما يلي
دعونا نلقي نظرة على كود runtime بالتفصيل
نظرًا لأن هذا عقد solidity، فيمكننا تقسيمه إلى كود بايت قابل للتنفيذ (executable bytecode) وبيانات وصفية للعقد كما هو موضح سابقًا
Runtime code := 0x608060405236601c57600080546001600160a01b03191633179055005b600080fdfe
Metadata := 0xa2646970667358221220e9b731ab28726d97cbf5219f1e5eaec508f23254c60b15ed1d3456572547c5bf64736f6c63430008070033a2646970667358221220e9b731ab28726d97cbf5219f1e5eaec508f23254c60b15ed1d3456572547c5bf64736f6c63430008070033.
Solidityدعنا نتعمق في ما يفعله كود runtime باستخدام مخرجات أكواد evm. لقد تم تقسيمها لتبسيطها.
أولاً نقوم بتهيئة مؤشر الذاكرة الحرة.
[00] PUSH1 80
[02] PUSH1 40
[04] MSTORE
ASMنحن هنا نتحقق مما إذا كانت البيانات قد تم إرسالها مع المعاملة، وإذا كان الأمر كذلك فإننا ننتقل بواسطة JUMP إلى عداد البرنامج (program counter) أو بإختصار (PC) أو 0x1c حيث نعود في حال خطأ. الطريقتان الوحيدتان الصالحتان لتلقي العقد للبيانات هما الوظائف العادية و fallback . لدينا فقط وظيفة استلام، لذلك لا توجد طريقة صالحة للعقد لتلقي بيانات الإستدعاء (calldata).
[05] CALLDATASIZE
[06] PUSH1 1c
[08] JUMPI
ASMثم لدينا الكود الذي يخزن 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
ASMهذا هو JUMPDEST 0x1c للحالة التي تم فيها إرسال بيانات الإستدعاء (calldata).. المعاملة لن تكتمل.
[1c] JUMPDEST
[1d] PUSH1 00
[1f] DUP1
[20] REVERT
[21] INVALID
ASMمنشئ مع المعلمات (Constructor with parameters)
يتم ترميز العقود التي تحتوي على وسيطات المُنشئ (constructor arguments) بشكل مختلف قليلاً. من المتوقع أن يتم إلحاق معلمات المُنشئ (constructor parameters) في نهاية كود الإنشاء (بعد كود runtime) وترميز ABI.
تضيف Solidity على وجه الخصوص فحصًا إضافيًا للتأكد من أن طول معلمة المُنشئ هو على الأقل طول وسيطات المُنشئ (constructor arguments) المتوقعة، وإلا فسيتم إرجاعه بخطأ.
دعونا نرى مثالا بسيطا. نحن لا نقوم بتضمين أي كود runtime من أجل البساطة. الكود الوحيد الذي نقوم بتضمينه موجود في المُنشئ، وهو ليس جزءًا من كود runtime.
// optimizer: 200contract MinimalLogic {
uint256 private x;
constructor (uint256 _x) payable {
x =_x;
}
}
Solidityكود الإنشاء هو 608060405260405160893803806089833981016040819052601e916025565b600055603d565b600060208284031215603657600080fd5b5051919 050565b603f80604a6000396000f3fe6080604052600080fdfea26469706673582212204a131c1478e0e7bb29267fd8f6d38a660b40a25888982bd6618b720 d4498b6b464736f6c63430008070033
عند تقسيمه نحصل على
"Init code": 0x608060405260405160893803806089833981016040819052601e916025565b600055603d565b600060208284031215603657600080fd5b5051919050565b603f80604a6000396000f3fe"Runtime code (metadata only)": 0x6080604052600080fdfea26469706673582212204a131c1478e0e7bb29267fd8f6d38a660b40a25888982bd6618b720d4498b6b464736f6c63430008070033"Constructor arguments are missing!"
Solidityسيؤدي تنفيذ كود الإنشاء بهذه الطريقة إلى الرجوع بخطأ (revert ) داخل كود init لأنه يتوقع استخدام 32 بايت على الأقل بعد كود runtime كـ uint256 _x. سنرى هذا بمزيد من التفصيل عند تقسيم كل كود opcode. في الوقت الحالي سنقوم بإلحاق كود الإنشاء بتشفير ABI للـ uint256(1) لاستخدامه كـ _x.
الآن، كود البايت الذي تم تصحيحه
608060405260405160893803806089833981016040819052601e916025565b600055603d565b600060208284031215603657600080fd5b5051919050565b603f80604a6000396000f3fe6080604052600080fdfea26469706673582212204a131c1478e0e7bb29267fd8f6d38a660b40a25888982bd6618b720d4498b6b464736f6c634300080700330000000000000000000000000000000000000000000000000000000000000001
Solidityدعونا نحلل هذا باستخدام مخرجات evm codes
الخطوة 1: تهيئة مؤشر الذاكرة الحرة
كالعادة مع عقود Solidity، نقوم بتهيئة مؤشر الذاكرة الحرة بالرقم 6080604052.
الخطوة 2: احصل على طول معلمة المنشئ (constructor parameter)
// 6040 51 6089 38 03
PC OPCODE
[05] PUSH1 40
[07] MLOAD
[08] PUSH1 89
[0a] CODESIZE
[0b] SUB
ASMهنا PUSH1 40 و MLOAD يقوم بتحميل مؤشر الذاكرة الحرة لاستخدامه لاحقًا. بعد ذلك PUSH1 89 تقوم بدفع طول كود الإنشاء (بدون معلمات المُنشئ (constructor parameters)) ثم يتم إستدعاء CODESIZE (وهذا يتضمن معلمة المُنشئ) و أخيرا SUB تقوم نطرح الاثنين للحصول على طول معلمات المنشئ.
الخطوة 3: انسخ معلمة المُنشئ إلى الذاكرة
// 80 6089 83 39
PC OPCODE
[0c] DUP1
[0d] PUSH1 89
[0f] DUP4
[10] CODECOPY
ASMنحن هنا نجهز المكدس لـ CODECOPY. نقوم بتكرار نتيجة الطرح من الأعلى باستخدام DUP1 و من ثم ندفع بـ 0x89 (طول كود الإنشاء (بدون وسيطات المُنشئ ) إلى المكدس باستخدام PUSH1 89. و بعد ذلك نستخدم DUP4، لجلب إزاحة الذاكرة (memory offset) إلى أعلى المكدس. و أخيرا نستخدم CODECOPY لنسخ معلمة المنشئ إلى الذاكرة عند مؤشر الذاكرة الحرة.
الخطوة 4: تحديث مؤشر الذاكرة الحرة
بعد كتابة الكود إلى الذاكرة، تقوم Solidity بتحديث مؤشر الذاكرة الحرة على النحو التالي.
// 81 01 6040 81 90 52
PC OPCODE
[11] DUP2
[12] ADD
[13] PUSH1 40
[15] DUP2
[16] SWAP1
[17] MSTORE
ASMنقوم بذلك هنا عن طريق إضافة طول معلمة المنشئ (0x20) الذي قمنا بتكراره مسبقًا إلى مؤشر الذاكرة الحرة (0x80) ثم ترتيبه مع عمليات Dup2 وSwap1 قبل استدعاء MSTORE الذي يخزن القيمة الجديدة (0xa0) كمؤشر للذاكرة الحرة.
بعد ذلك لدينا سلسلة من العمليات الديناميكية وعمليات JUMP التي لا يتم تنفيذها بشكل تسلسلي بل بناءً على بعض الشروط. دعونا نتعمق أكثر. الخطوات مرقمة بحيث يمكنك متابعتها بالتسلسل دون البحث عن JUMPDEST المطلوب.
يمكنك استخدام رابط لهذا الـbytecode لتجربته بنفسك أيضًا.
الخطوة 5: انتقل إلى JUMPDEST الخاص بـ SSTORE
// 601e 91 6025 56
PC OPCODE
[18] PUSH1 1e
[1a] SWAP2
[1b] PUSH1 25
[1d] JUMP // jump to JUMPDEST 0x25
Solidityنريد الانتقال إلى عداد البرنامج (program counter) الذي ينفذ العمليات لتخزين معلمة المُنشئ (constructor parameter) في وحدة التخزين (storage).
ندفع 1e إلى المكدس. 1e هو الموضع في عداد البرنامج حيث يتم تنفيذ SSTORE فعليًا، ولكن علينا أولاً التحقق من أن معلمة المنشئ المنسوخة لا تقل عن 32 بايت. تبدأ هذه العملية عند عداد البرنامج 0x25، وهو JUMPDEST أعلاه.
الخطوة 8: تخزين وسيطة المُنشئ (constructor arg) في فتحة التخزين 0.
هذا هو JUMPDEST 0x1e، ويتم تنفيذ JUMPDEST 0x25 أولاً وهو أدناه. لاحظ أن هذه هي الخطوة 8، والقسم السابق كان الخطوة 5. ويتم تنفيذها فقط في حالة اكتمال الشروط الواردة في 6 و7 بنجاح. نقدمه هنا من أجل الحفاظ على نفس تسلسل الـ bytecode من قبل الcompiler .
// 5b 6000 55 603d 56
PC OPCODE
[1e] JUMPDEST
[1f] PUSH1 00
[21] SSTORE
[22] PUSH1 3d
[24] JUMP
Solidityهنا ندفع 0x00 وهي فتحة التخزين التي سنقوم بتخزين _x فيها و نستدعي SSTORE. ثم نقوم بعملية دفع PUSH1 للـ Jump و هي الوجهة النهائية للـ codecopy و العودة .
الخطوة 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
Solidityهنا نتحقق من أن معلمة المُنشئ لا تقل عن 32 بايت.
أولاً، ندفع 0x00 إلى المكدس (لإستخدامها لاحقا)، وندفع الحد الأدنى للطول المقبول 0x20 وهو (32 بايت) إلى المكدس. بعد ذلك يمكننا مقارنة طول معلمة المُنشئ (constructor parameter ) عن طريق التحقق من إزاحتها ومؤشر الذاكرة الحرة الحالي الذي دفعناه إلى المكدس مسبقًا. لذلك نستخدم DUP3 للحصول على الإزاحة للمكدس ثم DUP5 لإيصال مؤشر الذاكرة الحرة الحالي إلى أعلى المكدس.
يؤدي استدعاء SUB إلى طرح الطول ودفعه إلى المكدس. يمكننا الآن استدعاء SLT مباشرة (إختصار لعلامة “أقل من” مُوقعه signed less than ) للتحقق مما إذا كان الطول يساوي 32 بايت ودفع 0 إذا كان خطأ و1 إذا كان صحيحًا، يتحقق كود التشغيل ISZERO مما إذا كان الجزء العلوي من المكدس (نتيجة SLT) هو 0، و يستخدم pops لدفع النتيجة المنطقية إلى المكدس، و من ثم ندفع موقع JUMP التالي إلى المكدس وننتقل إليه إذا أعاد ISZERO 1. وإلا فإننا نعود لتجنب التنفيذ باستخدام بيانات استدعاء غير صالحة.
الخطوة 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
Solidityهنا نستخدم POP لإخراج 0 (عداد البرنامج 26 من الخطوة 6) لأننا لم نعد بحاجة إليه. نستخدم MLOAD لتحميل معلمة المُنشئ إلى المكدس و تفريغ إزاحة ذاكرة معلمات المُنشئ (constructor parameters) حيث لم تعد هناك حاجة إليها أيضًا.
الخطوة 9: نسخ كود runtime إلى الذاكرة وإعادته.
هذا هو 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
Solidityهنا نعيد كود runtime للعقد كالمعتاد من الذاكرة.
الذاكرة قبل تنفيذ RETURN مباشرة
0x00 إلى 0x40 هو كود runtime (الفارغ) والـbytecode لبيانات التعريف. 0x40 يحتوي على مؤشر الذاكرة الحرة. يحتوي 0x80 على وسيطة المُنشئ، uint256(1).
0x00<->0x20 = 0x6080604052600080fdfea26469706673582212208f9ffa7a3ab43f0ff61d30330x20<->0x40 = 0x624bf0e9d398f9a91213656b13d9ffc8fd90fdbc64736f6c63430008070033000x40<->0x60 = 0x00000000000000000000000000000000000000000000000000000000000000a00x60<->0x80 = 0x00000000000000000000000000000000000000000000000000000000000000000x80<->0xa0 = 0x0000000000000000000000000000000000000000000000000000000000000001
ASMخاتمة
يتضمن نشر العقد الذكي بضع عمليات منخفضة المستوى لا تتطرق لها معظم اللغات. لقد تعلمنا كيفية تنفيذ العقود الذكية باستخدام كود الإنشاء المُرسل إلى العنوان الفارغ، والأجزاء المختلفة لكود الإنشاء هذا، وأدوارها عند نشر العقد وكيفية عملها معًا. لقد رأينا كيف يتم تخزين وسيطات المُنشئ (constructor arguments) والتحقق من صحتها واستخدامها لإعداد العقد.
إطرح رأيك ؟
أظهر التعليقات / إنرك تعليق