آنچه در این مقاله میخوانید [پنهانسازی]
نوشتن قرارداد هوشمند فقط بلد بودن سینتکس سالیدیتی نیست. خیلی از باگ ها و هک های بزرگ نه به خاطر پیچیدگی، بلکه به خاطر اشتباهات ساده و تکراری رخ داده اند. شناخت اشتباهات سالیدیتی کمک می کند قبل از اینکه قرارداد روی شبکه اصلی deploy شود، جلوی هزینه های سنگین، از دست رفتن سرمایه و خراب شدن اعتبار پروژه را بگیری.
سرفصل های مقاله
- درک اشتباه از ماهیت تغییرناپذیر قراردادها
- نادیده گرفتن امنیت به خاطر سادگی کد
- استفاده نادرست از require و revert
- اعتماد بیش از حد به msg.sender
- استفاده خطرناک از tx.origin
- مدیریت نادرست دسترسی ها
- حلقه های بدون محدودیت
- مصرف بی رویه storage
- نادیده گرفتن تفاوت memory و calldata
- ارسال اتر به روش ناامن
- نداشتن محافظ در برابر reentrancy
- اعتماد به داده های خارجی بدون بررسی
- نداشتن تست کافی
- تست نکردن روی شبکه های آزمایشی
- پیچیده کردن بیش از حد قرارداد
- نادیده گرفتن رویدادها
- به روز نکردن دانش نسبت به نسخه های جدید
- اعتماد بیش از حد به audit
- جمع بندی
درک اشتباه از ماهیت تغییرناپذیر قراردادها
یکی از اولین اشتباهات سالیدیتی این است که توسعه دهنده مثل بک اند معمولی فکر می کند. در بلاکچین، بعد از deploy دیگر نمی توانی کد را ویرایش کنی. اگر باگ جدی داشته باشی، یا باید قرارداد جدید بسازی یا با workaround های پرریسک جلو بروی. این یعنی طراحی و تست قبل از انتشار حیاتی است.
نادیده گرفتن امنیت به خاطر سادگی کد
بعضی توسعه دهنده ها فکر می کنند چون قرارداد کوچک است، امن هم هست. این یکی از خطرناک ترین اشتباهات سالیدیتی است. حتی یک تابع ساده transfer می تواند نقطه حمله باشد. امنیت به حجم کد ربطی ندارد، به دقت طراحی مربوط است.
استفاده نادرست از require و revert
خیلی وقت ها شرط ها در جای اشتباه نوشته می شوند. وقتی require دیر اجرا شود و بعد از چند عملیات پرهزینه fail کند، مقدار زیادی گس هدر می رود. بررسی های حیاتی باید اول تابع انجام شوند. این یک اصل ساده است که خیلی وقت ها نادیده گرفته می شود.
اعتماد بیش از حد به msg.sender
msg.sender همیشه همان کسی نیست که انتظارش را داری. در فراخوانی های تو در تو یا استفاده از قراردادهای دیگر، ممکن است msg.sender یک قرارداد باشد نه کاربر نهایی. اشتباه در درک این موضوع می تواند باعث باگ های امنیتی جدی شود. این مورد یکی از اشتباهات سالیدیتی است که تازه کارها زیاد مرتکب می شوند.
استفاده خطرناک از tx.origin
استفاده از tx.origin برای احراز هویت تقریبا همیشه اشتباه است. این متغیر در حملات فیشینگ قراردادهای هوشمند به شدت خطرناک است. هنوز هم بعضی توسعه دهندگان از آن استفاده می کنند، در حالی که سال هاست به عنوان یک ضد الگو شناخته می شود.
مدیریت نادرست دسترسی ها
فراموش کردن modifier های دسترسی یا نوشتن آنها به شکل اشتباه یکی از رایج ترین اشتباهات سالیدیتی است. اگر تابع حساس بدون محدودیت باقی بماند، کل قرارداد در معرض خطر است. دسترسی ها باید شفاف، تست شده و ساده باشند.
حلقه های بدون محدودیت
حلقه روی آرایه هایی که اندازه آنها رشد می کند، یک بمب ساعتی است. ممکن است امروز کار کند، اما فردا به خاطر تمام شدن گس هیچ تراکنشی نتواند آن تابع را اجرا کند. این اشتباه باعث می شود قرارداد عملا قفل شود.
مصرف بی رویه storage
storage گران است، اما خیلی از توسعه دهندگان به آن توجه نمی کنند. ذخیره داده های غیرضروری، آرایه های بزرگ یا اطلاعاتی که می توانند off chain باشند، هزینه قرارداد را بالا می برد. این یکی از اشتباهات سالیدیتی است که تاثیر اقتصادی مستقیم دارد.
نادیده گرفتن تفاوت memory و calldata
استفاده اشتباه از memory به جای calldata در توابع خارجی باعث مصرف گس بیشتر می شود. این تفاوت شاید کوچک به نظر برسد، اما در قراردادهای پرترافیک تاثیر زیادی دارد. توجه به این جزئیات نشانه حرفه ای بودن توسعه دهنده است.
ارسال اتر به روش ناامن
استفاده اشتباه از call، send یا transfer می تواند هم باعث باگ امنیتی شود و هم قرارداد را شکننده کند. توسعه دهنده باید بداند هرکدام چه محدودیت ها و ریسک هایی دارند. انتخاب نادرست روش ارسال اتر یکی از اشتباهات سالیدیتی پرهزینه است.
نداشتن محافظ در برابر reentrancy
حمله reentrancy یکی از معروف ترین حملات بلاکچین است، اما هنوز هم قربانی می گیرد. تغییر state بعد از ارسال اتر یا فراخوانی خارجی، یک اشتباه کلاسیک است. ترتیب عملیات باید درست باشد تا این حمله ممکن نشود.
اعتماد به داده های خارجی بدون بررسی
داده هایی که از بیرون بلاکچین می آیند قابل اعتماد نیستند مگر اینکه مکانیزم مشخصی داشته باشند. استفاده نادرست از oracle ها یا فرض صحت داده خارجی می تواند منجر به تصمیم های اشتباه داخل قرارداد شود. این مورد یکی از اشتباهات سالیدیتی است که در پروژه های مالی خطرناک تر است.
نداشتن تست کافی
خیلی از توسعه دهندگان تست را جدی نمی گیرند یا فقط چند سناریوی ساده را تست می کنند. قرارداد هوشمند بدون تست یعنی ریسک بالا. تست باید شامل سناریوهای خطا، ورودی های غیرمنتظره و شرایط مرزی باشد.
تست نکردن روی شبکه های آزمایشی
رفتن مستقیم به شبکه اصلی بدون تست کافی روی testnet یک اشتباه بزرگ است. testnet دقیقا برای همین ساخته شده. نادیده گرفتن آن معمولا از عجله یا اعتماد به نفس بیش از حد می آید و هزینه اش سنگین است.
پیچیده کردن بیش از حد قرارداد
قراردادهای بیش از حد پیچیده سخت تر تست می شوند، گران تر هستند و احتمال باگ در آنها بالاتر است. سادگی یک مزیت امنیتی است. یکی از اشتباهات سالیدیتی این است که همه چیز را داخل یک قرارداد بزرگ جا بدهی.
نادیده گرفتن رویدادها
event ها فقط برای زیبایی نیستند. آنها ابزار ارتباط قرارداد با دنیای خارج هستند. نداشتن event مناسب باعث می شود مانیتورینگ، تحلیل و اشکال زدایی سخت شود. این اشتباه شاید امنیتی نباشد، اما هزینه توسعه را بالا می برد.
به روز نکردن دانش نسبت به نسخه های جدید
سالیدیتی در حال تغییر است. ویژگی ها، محدودیت ها و best practice ها عوض می شوند. نوشتن کد با فرض های قدیمی می تواند مشکل ساز شود. یکی از اشتباهات سالیدیتی این است که توسعه دهنده دانش خود را به روز نگه ندارد.
اعتماد بیش از حد به audit
audit مهم است، اما معجزه نمی کند. اگر طراحی از ابتدا غلط باشد، audit فقط بخشی از مشکلات را می گیرد. مسئولیت اصلی امنیت همیشه با توسعه دهنده است، نه ابزار یا تیم audit.
جمع بندی
اشتباهات سالیدیتی بیشتر از اینکه فنی باشند، ذهنی و طراحی هستند. عجله، ساده انگاری، نادیده گرفتن امنیت و نداشتن تست کافی ریشه بسیاری از مشکلاتند. توسعه دهنده ای که این اشتباهات رایج را بشناسد و از ابتدا جلوی آنها را بگیرد، قراردادهایی می نویسد که هم امن تر هستند و هم اعتماد کاربران را جلب می کنند. در بلاکچین، پیشگیری همیشه ارزان تر از درمان است.






