عندما تقوم Solidity بإنشاء كود ثانوي للعقد الذكي الذي سيتم نشره، فإنها تقوم بإلحاق البيانات التعريفية حول التجميع في نهاية الكود الثانوي. سوف نقوم بفحص البيانات الموجودة في هذا الرمز الثانوي.
عقد ذكي بسيط
دعونا نلقي نظرة على مخرجات المترجم لأبسط عقد ذكي ممكن
//SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
contract Empty {
constructor() payable {}
}
Solidityالعقد لا يفعل شيئا حرفيا. يمكننا تجميعه لرؤية كود init باستخدام solc –optimize-runs 1000 –bin C.sol . نحصل على الإخراج التالي
======= C.sol:Empty =======
Binary:
6080604052603e80600f5f395ff3fe60806040525f80fdfea26469706673582212203082dbb4f4db7e5d53b235f44d3e38f839dc82075e2cda9df05b88e6585bca8164736f6c63430008140033
Solidityيبدو هذا كبيرًا جدًا بالنسبة للعقد الذي لا يفعل أي شيء، أليس كذلك؟ دعونا نفهم ما هو كل هذا الرمز الثانوي.
عندما نقوم بتجميع الكود باستخدام solc –optimize-runs 1000 –bin –no-cbor-metadata C.sol، نحصل على الإخراج التالي:
======= C.sol:Empty =======
Binary:
6080604052600880600f5f395ff3fe60806040525f80fd
Solidityوهذا أصغر بكثير! إذن ما هي كل تلك المعلومات الإضافية؟
البيانات الوصفية الصلبة
افتراضيًا، يقوم برنامج التحويل البرمجي Solidity بإلحاق البيانات الوصفية في نهاية رمز initcode “الفعلي”، والذي يتم تخزينه في blockchain عندما ينتهي المُنشئ من التنفيذ. إليك الكود “الإضافي” أدناه:
fea26469706673582212203082dbb4f4db7e5d53b235f44d3e38f839dc82075e2cda9df05b88e6585bca8164736f6c63430008140033
mukaebالبايتتان الأخيرتان 0033 تعني “النظر للخلف 0x33 بايت، هذه هي البيانات التعريفية.” يشير هذا إلى كافة التعليمات البرمجية الموجودة بين البادئة fe (وهي شفرة التشغيل غير الصالحة) والنهاية 0033 . يمكننا التحقق من أن هذا هو بالفعل 0x33 بايت.
# fe and 0033 are not included
>>>hex(len('a26469706673582212203082dbb4f4db7e5d53b235f44d3e38f839dc82075e2cda9df05b88e6585bca8164736f6c6343000814') // 2)
# '0x33'
mukaebإذن ما هي هذه السلسلة 0x33 (51 علامة عشرية)؟
يمكننا الحصول على تلميح إذا أجرينا تغييرًا صغيرًا يبدو غير مهم على الكود المصدري. التغيير هو حرفيًا مجرد تعليق إضافي.
//SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
contract Empty {
// nothing
constructor() payable {}
}
mukaebلقطة الشاشة التالية هي قبل وبعد.
يمكنك أن ترى أن الأقسام التي تحتها خط قد تغيرت على الرغم من عدم تغيير وظيفة التعليمات البرمجية. سنشرح الكود الموجود في المربعات في القسم التالي.
فك تشفير البيانات الوصفية
في البداية، سيبدو الأمر وكأننا نلتقط سلاسل فرعية بطريقة سحرية من فراغ؛ تحمل معنا.
دعونا نلقي نظرة على الشكل السداسي الموجود في المربع الأزرق أعلاه
>>> bytes.fromhex("69706673").decode("ASCII")
'ipfs'
mukaebبعد ذلك، دعونا نلقي نظرة على الكود الموجود في المربع الأحمر
>>> bytes.fromhex("736f6c63").decode("ASCII")
'solc'
mukaebوهذا يعطينا فكرة عما تحتويه هذه البيانات: تجزئة IPFS وإصدار المترجم القوي.
تجزئة IPFS
يمكن وضع القسم الذي تحته خط باللون الأصفر، بالإضافة إلى المربع الفيروزي، في نص بايثون التالي (لاحظ أننا نستخدم إصدار الكود مع التعليق // nothing )
import base58
hex_ipfs_hash = "12206a68b6b8bcc01ba559ec3adf7a387b6c4210a5dc69a05d038e9d17cae3fa373b"
bytes_str = bytes.fromhex(hex_ipfs_hash)
print(base58.b58encode(bytes_str).decode("utf-8"))
# QmVW2XyafSxDtiSqirJRauuT5SaQtGnQYsxxyYHrFmRTEa
mukaebQm …RTEa هو تجزئة IPFS لملف البيانات التعريفية الذي ينتجه المترجم. يتم ترميز هذا القسم من التعليمات البرمجية (الفيروزي والأصفر) بشكل مختلف عن المربعات أعلاه.
على وجه التحديد، فإن تجزئة IPFS (الفيروزي والأصفر) هي نسخة مشفرة من نوع base58 للبيانات السداسية “1220…RTEa”.
هذا هو تجزئة IPFS الذي ستحصل عليه إذا قمت بوضع ملف JSON من برنامج التحويل البرمجي Solidity على IPFS. هنا ملف JSON المعني.
يمكننا تخزين ملف JSON كملف فعلي ثم التحقق من صحة تطابق التجزئة مع تلك التي أنشأناها في بايثون أعلاه. ستحتاج إلى تثبيت أداة سطر الأوامر ipfs ( كيفية التثبيت ).
mkdir out
solc --optimize-runs 1000 --bin --metadata C.sol --output-dir out
# Compiler run successful. Artifact(s) can be found in directory "out".
ipfs add -qr --only-hash out/Empty_meta.json
# QmVW2XyafSxDtiSqirJRauuT5SaQtGnQYsxxyYHrFmRTEa
mukaebوهذا يطابق التجزئة من وقت سابق.
لن يسبب هذا تصادمات التجزئة؟
إذا قام عقدان لهما نفس التعليمات البرمجية المصدر وتكوينات المترجم بتخزين كود المصدر الذي تم التحقق منه على IPFS، فسوف تتصادم تجزئة IPFS، ولكن هذا أمر مرغوب فيه لأنه يوفر مساحة التخزين بالفعل. يتم تحديد العقود الذكية بشكل فريد من خلال مزيج من معرف السلسلة وعنوانها، وليس محتوى IPFS.
الحصول على نسخة سوليدتي
mkdir out
solc --optimize-runs 1000 --bin --metadata C.sol --output-dir out
# Compiler run successful. Artifact(s) can be found in directory "out".
ipfs add -qr --only-hash out/Empty_meta.json
# QmVW2XyafSxDtiSqirJRauuT5SaQtGnQYsxxyYHrFmRTEa
mukaebأخيرًا، إذا قمنا بتحويل القسم الموجود في المربع البرتقالي، فسنرى نسخة سوليدتي.
>>> 0x00 # solidity is version 0
0
>>> 0x08 # major version
8
>>> 0x14 # minor version
20
# correct, we used solidity 0.8.20
mukaebلماذا توجد البيانات الوصفية للعقد الذكي؟
تضيف بيانات التعريف هذه 53 بايت إضافية إلى تكلفة النشر، وهو ما يترجم إلى 10600 غاز إضافية (200 لكل بايت كود) + تكلفة بيانات الاتصال (16 غاز لكل بايت غير الصفر، و4 غاز لكل صفر بايت). وهذا يترجم إلى ما يصل إلى 848 غازًا إضافيًا في تكلفة بيانات المكالمات.
فلماذا إدراجها؟
يتيح ذلك التحقق بدقة من رمز العقد الذكي. تشتمل البيانات التعريفية JSON التي يخرجها المترجم على تجزئة كود المصدر. لذا، إذا تغير كود المصدر قليلاً، فسيتغير ملف البيانات التعريفية JSON وستتغير تجزئة IPFS الخاصة به.
خدعة غريبة لخفض الغاز عبر تجزئة IPFS
إحدى الطرق الواضحة لتقليل تكلفة الغاز في وقت النشر هي استخدام خيار –no-cbor-metadata . ولكن إذا كنت بحاجة إلى هذا للتحقق من العقد، فلا يزال بإمكانك تقليل تكلفة الغاز عن طريق التعدين لتجزئة IPFS التي تحتوي على الكثير من البايتات الصفرية. عند نشر العقد، ستؤدي البايتات الصفرية إلى تقليل تكلفة بيانات الاتصال. نظرًا لأن كود المصدر مجزأ، بما في ذلك التعليقات، فهذا يعني أنه يمكن للمرء التنقيب عن التعليقات التي تؤدي إلى تجزئات IPFS الموفرة للغاز والتي سيتم إلحاقها بالعقد. لاحظ أن هذا يعني أننا نريد أن يحتوي التمثيل السداسي للتجزئة على أصفار، وليس ترميز base58.
مزيد من الموارد
يمكنك رؤية كافة الخيارات لمعالجة هذه البيانات الوصفية في وثائق سوليدتي ذات الصلة . يوفر Sourcify أداة لتحليل البيانات الوصفية للعقود الذكية الحالية.
إطرح رأيك ؟
أظهر التعليقات / إنرك تعليق