كما أوضحنا مرارًا وتكرارًا ، من المحتمل أن يكون تطوير العقود الذكية في غاية الخطورة. هذا على عكس الجزء الأكبر من تطوير الويب ، حيث يمكن هدم البيئات و الأكواد وتغييرها ونشرها بسهولة مرة أخرى. ولهذا السبب ، فإن كونك مطورًا ذكيًا للعقود يعني أن تكون مدركًا تمامًا للطرق التي يمكن من خلالها استغلال الأكواد الخاصة بك وإنشاء ميزات أمان لضمان عدم حدوث هجمات غير متوقعة.
خلال الأقسام القليلة التالية ، سنناقش أمان العقد الذكي. سنبدأ من خلال تجاوز بعض المخاطر والهجمات الشائعة باستخدام Solidity (كلغة) والعقود الذكية بشكل عام. .
نصائح Solidity
استخدام إصدار محدد لـ Compiler
من الطرق الشائعة استخدام ^ للإشارة إلى أقل إصدار من Compiler سيعمل معه عقدك. ومع ذلك ، إذا كان لديك مجموعة اختبار قمت بإنشائها وفقًا لعقدك ، فلا يجب عليك تضمين ^ وإدراج إصدار Compiler فقط.
pragma solidity ^0.4.4; // Possibly dangerous
pragma solidity 0.4.4; // Better
أسماء المتغيرات المضمنة
يمكن تظليل (shadowed) المتغيرات المضمنة ، كن حذرًا عند تسمية وظائفك:
function revert() internal pure {} // Possibly dangerous
function myRevert() internal pure {} // Better
الاستخدام السليم require و assert و revert
require(msg.sender == owner)
- التحقق من صحة المدخلات وإرجاع المكالمات الخارجية والمتغيرات قبل تغيير الحالة
- إرجاع مبالغ الغاز المتبقي عند الفشل
- يجب استخدامه في كثير من الأحيان وفي بداية الوظائف
assert(age > 100)
- التحقق من صحة الثوابت ، والمواقف التي لا ينبغي أن تحدث أبدًا والمتغيرات بعد تغير الحالة
- يستهلك كل الغاز عند الفشل
- يجب استخدامه بشكل أقل و في نهاية الوظائف
revert()
- إرجاع المعاملة الحالية لحالتها الأصليه
- إرجاع مبالغ الغاز المتبقي عند الفشل
- تستخدم ضمن عبارات if-else
استخدم المعدلات فقط لعمليات التحقق من الصحة
ما عليك سوى إجراء عمليات التحقق من الصحة داخل المُعدِّلات وتجنب المكالمات الخارجية عليها:
modifier partnerShare() { // Possibly dangerous partner.transfer(msg.value / 10); _; }
modifier onlyOwner() { // Better! require(msg.sender == owner); _; }
السحب أفضل من الدفع
إعطاء الأولوية لتلقي مكالمات العقد على إجراء مكالمات العقد:
function doRefund() external onlyOwner { // Possibly dangerous uint refund; for(uint i = 0; i < refunds.length; i++) { refund = refunds[i].value; refunds[i].value = 0; refunds[i].addr.transfer(refund); } }
function withdrawRefund() external { // Better! require(refunds[msg.sender] > 0); uint refund = refunds[msg.sender]; refunds[msg.sender] = 0; msg.sender.transfer(refund); }
وظائف Fallback وطول البيانات
اجعل الوظائف Fallback بسيطة وتحقق من طول البيانات:
fallback() payable external { // Possibly dangerous balances[msg.sender] += msg.value; }
receive() external { // Better! balances[msg.sender] += msg.value; } fallback() external { require(msg.data.length == 0); emit LogDeposit(msg.sender); }
تحقق من التاثيرات الناتجة من التفاعلات
تجنب تغييرات الحالة بعد المكالمات الخارجية ، لتجنب أشياء مثل the DAO hack:
function withdraw(uint amount) public { // Possibly dangerous require(balances[msg.sender] >= amount); msg.sender.call.value(amount)(""); balances[msg.sender] -= amount; }
function withdraw(uint amount) public { // Better! require(balances[msg.sender] >= amount); balances[msg.sender] -= amount; msg.sender.call.value(amount)(""); }
الاستخدام السليم call ، delegatecall بدلاً من send، transfer
بعد تشعب إسطنبول الصلب ، يوصى بعدم استخدام send و transfer ، وبدلاً من ذلك استخدام call.value
contract Vulnerable { // Possibly dangerous function withdraw(uint256 amount) external { // This forwards 2300 gas, which may not be enough if the recipient // is a contract and gas costs change. msg.sender.transfer(amount); } }
contract Fixed { // Better! function withdraw(uint256 amount) external { // This forwards all available gas. Be sure to check the return value! (bool success, ) = msg.sender.call.value(amount)(""); require(success, "Transfer failed."); } }
ثم الاستخدام السليم call و delegatecall:
addr.call(abi.encodeWithSignature("f(uint)", a));
- تستخدم لاستدعاء الوظائف وإرسال الأثير
- يسمح بتحديد الغاز
- إرجاع قيمة منطقية
- لا ينشر الاستثناءات
addr.delegatecall(abi.encodeWithSignature("f(uint)", a));
- تُستخدم لتشغيل الوظائف ضمن سياق المتصل (ميزة المكتبة)
- يسمح بتحديد الغاز
- إرجاع قيمة منطقية
- لا ينشر الاستثناءات
إضافة تعليق