تحلیلها
چکلیست آمادگی برای ممیزی قرارداد هوشمند
۳۰ بررسی پیش از ممیزی قرارداد هوشمند: اسکوپ و مستندات، تستها، تستهای فاز و اینوریانت Foundry، تریاژ Slither، کنترل دسترسی، یکپارچهسازیها و دیپلوی.
تیم مهندسی sigmacode.io8 دقیقه مطالعه
در این صفحه (9)
ممیزی قرارداد هوشمند پرهزینه است و بازه زمانی بسیار محدودی دارد. ممیزان تعداد مشخصی روز در اختیار دارند و هر ساعتی که صرف فهمیدن این شود که کد قرار است چه کار کند، دنبال کردن یک commit در حال حرکت یا نوشتن تستهایی که شما ننوشتهاید، ساعتی است که صرف منطقی نمیشود که واقعاً میتواند وجوه را از دست بدهد. سپس گزارش یافتهها پر میشود از NatSpecهای ناقص، pragmaهای شناور و مسیرهای revert تستنشده، بهجای مسائلی که برای یافتنشان هزینه میپردازید.
یک کدبیس خوب آمادهشده با همان بودجه ممیزی عمیقتری میگیرد. این چکلیست آنچه را که پیش از تحویل کد به یک ممیز خارجی برقرار میکنیم گرد میآورد: ۳۰ بررسی مشخص در زمینه مستندات، بهداشت کد، تست، کنترل دسترسی، یکپارچهسازیها، دیپلوی و لجستیک. این جایگزین ممیزی نیست؛ تضمین میکند زمان ممیزی به جایی میرود که مهم است.
۱. اسکوپ و مستندات#
ممیزان فقط میتوانند رفتار را در برابر یک قصدی که میفهمند راستیآزمایی کنند، پس قصد باید نوشته شود.
- یک هش commit را فریز کنید. یک commit تگخورده به ممیزان بدهید و کد تحت بازبینی را در طول قرارداد تغییر ندهید. اصلاحات روی یک برنچ جدا میروند و در دور بازبینی اصلاحات بررسی میشوند.
- فهرست فایلهای اسکوپ را با nSLOC منتشر کنید. هر قرارداد درون اسکوپ را با مسیر و خطوط کد منبع نرمالشدهاش فهرست کنید و صراحتاً بگویید چه چیزی خارج از اسکوپ است (کتابخانهها، ماکها، اسکریپتها، فایلهای ممیزیشده قبلی). ممیزان بر اساس nSLOC قیمت میدهند و برنامهریزی میکنند، پس یک شمارش دقیق از غافلگیری دو طرف جلوگیری میکند.
- یک نمای کلی معماری بنویسید. یک یا دو صفحه که قراردادها، نحوه فراخوانی یکدیگر، کدام قراردادها وجوه را نگه میدارند و جریانهای اصلی کاربر را توصیف میکند. یک دیاگرام ساده از فراخوانیها و جریانهای توکن، ساعتها مهندسی معکوس را از ممیزان میگیرد.
- رفتار و اینوریانتها را بهصورت نثر مشخص کنید. توصیف کنید هر تابع خارجی باید چه کار کند و چه ویژگیهایی باید همیشه صادق باشد، مثلاً «مجموع بالانسهای کاربر برابر
totalAssetsاست» یا «فقط تایملاک میتواند کارمزدها را تغییر دهد». این عبارتها مبنای بازبینی ممیزان و تستهای اینوریانت خود شما میشوند. - مسائل شناختهشده و ریسکهای پذیرفتهشده را مستند کنید. محدودیتهایی که از قبل میدانید و تصمیمهای طراحیای که میپذیرید را فهرست کنید، مانند مفاضلات تمرکز یا انواع توکن پشتیبانینشده. این آنها را بیرون از گزارش یافتهها نگه میدارد و به ممیزان نشان میدهد کجاها از قبل فکر کردهاید.
۲. بهداشت کد و تحلیل استاتیک#
نویز در کدبیس هزینه زمان ممیزی است و یافتههای کمارزش تولید میکند، پس آن را پیش از دیدن ممیزان حذف کنید.
- کامپایلر را پین کنید و به صفر هشدار برسید. از
pragma solidity 0.8.x;دقیق استفاده کنید بهجای^0.8.0شناور، و نسخه، تعداد اجرای آپتیمایزر،via_irوevm_versionدرfoundry.tomlرا با چیزی که دیپلوی خواهید کرد مطابق کنید. هر هشدار کامپایلر باید اصلاح یا آگاهانه توضیح داده شود. - وابستگیها را پین و فهرست کنید. OpenZeppelin Contracts و هر کتابخانه دیگر را روی یک ریلیز دقیق قفل کنید، مثلاً یک سابماژول تگخورده v5.x یا یک نسخه دقیق در
package.json. همه وابستگیها را در مستندات ممیزی فهرست کنید و فایلهایی که کپی یا تغییر دادهاید را علامت بزنید. - کد مرده، TODOها و خروجی دیباگ را حذف کنید. توابع استفادهنشده، کد کامنتشده، ایمپورتهای
console.logو helperهای تست باقیمانده را از قراردادهای اسکوپشده پاک کنید. TODOهای باز منطق ناتمام را نشان میدهند و همینطور گزارش خواهند شد. - NatSpec را روی توابع خارجی و عمومی کامل کنید. هر تابع خارجی و عمومی باید
@notice،@param،@returnو جایی که لازم است یادداشتی درباره کنترل دسترسی و revertها داشته باشد. از ارورهای سفارشی استفاده کنید و برای هر تغییر وضعیتی که خارج از زنجیره مهم است رویداد منتشر کنید. - Slither را اجرا و هر یافته را تریاژ کنید.
slitherرا روی commit فریزشده اجرا کنید و هر یافته را با یک خط توجیه بهعنوان اصلاحشده، مثبت کاذب یا پذیرفتهشده طبقهبندی کنید. یک ابزار دوم مانند Aderyn اغلب الگوهای متفاوتی میگیرد و به اشتراک گذاشتن خروجی تریاژشده به ممیزان میگوید کدام یافتههای خودکار از قبل رسیدگی شدهاند.
۳. تستها#
تستها به ممیزان نشان میدهند کد قرار است چگونه استفاده شود و به آنها اجازه میدهد بهسرعت اثبات مفهوم بنویسند.
- هر تابع خارجی را پوشش دهید و گزارش را منتشر کنید. هر تابع خارجی و عمومی حداقل به یک تست مسیر خوش نیاز دارد. با
forge coverageیک گزارش پوشش خط و شاخه تولید کنید و هر شاخه پوششنیافته را توضیح دهید بهجای پنهان کردنش. - مسیرهای revert و موارد مرزی را تست کنید. با
vm.expectRevertتأیید کنید که فراخوانیهای غیرمجاز، پارامترهای نامعتبر، مقادیر صفر و مقادیر مرزی با ارور سفارشی موردانتظار revert میکنند. مسیرهای revert تستنشده جایی است که باگهای اعتبارسنجی و کنترل دسترسی پنهان میشوند. - تستهای فورک را روی یکپارچهسازیهای واقعی اجرا کنید. اگر پروتکل با قراردادهای خارجی مانند DEXها، بازارهای وام یا اوراکلها صحبت میکند، روی فورک میننت یا L2 با شماره بلاکهای پینشده تست کنید. ماکها فقط ثابت میکنند کد شما با فرضهای شما درباره طرف مقابل کار میکند.
۴. تستهای فاز و اینوریانت#
فازینگ ترکیبهای ورودیای را پیدا میکند که هیچکس به آنها فکر نکرده و تستهای اینوریانت بررسی میکنند سیستم در تسلسلهای فراخوانی سازگار میماند.
- هر تابعی که ورودی عددی میگیرد را فاز کنید. برای مقادیر، تایماستمپها، محاسبات سهم و ریاضیات کارمزد تستهای فاز Foundry بنویسید و ورودیها را با
bound()محدود کنید، نهvm.assumeافراطی. جهت گرد کردن و سرریز در مقادیر حدی اهداف معمولاند. - تستهای اینوریانت stateful با handler بنویسید. از تست اینوریانت Foundry با قراردادهای handler استفاده کنید که سیستم را در تسلسلهای واقعی از چند بازیگر فراخوانی میکنند. پس از هر تسلسل فراخوانی، توانگری، حفظ ارزش و ویژگیهای کنترل دسترسی را بررسی کنید.
- اینوریانتهای نوشتهشده و مجموعه تست را همگام نگه دارید. هر اینوریانت در مشخصات شما باید به یک تست اینوریانت نگاشت شود و هر تست اینوریانت باید به مشخصات برگردد. مجموعه را با تعداد اجراها و عمق معنادار در CI اجرا کنید، نه فقط محلی با پیشفرضها.
۵. کنترل دسترسی و ارتقاپذیری#
توابع دارای امتیاز و ارتقاها میتوانند هر دارایی در سیستم را منتقل یا قفل کنند، پس مدل اعتماد باید صریح باشد.
- هر نقش و تابع دارای امتیاز را فهرست کنید. هر نقش را مستند کنید، کدام توابع را میتواند فراخوانی کند، چه کسی آن را دارد و بدترین سناریو در صورت لو رفتن کلیدش چیست. این بخش فرضهای اعتمادی است که ممیزان اول خواهند خواست.
- قدرت ادمین را پشت مولتیسیگ و تایملاک بگذارید. پارامترهای حساس و ارتقاها باید توسط یک مولتیسیگ مانند Safe کنترل شوند، با
TimelockControllerیا تأخیر معادل برای تغییراتی که وجوه کاربران را تحت تأثیر میگذارند. آستانه امضاکنندگان و تأخیر را مستند کنید. - لایهچینی استوریج را برای قراردادهای ارتقاپذیر بررسی کنید. برای پراکسیها، از قراردادهای ارتقاپذیر OpenZeppelin v5 با استوریج فضاینامی ERC-7201 استفاده کنید و ارتقاها را با ابزارهای OpenZeppelin upgrades اعتبارسنجی کنید. اگر ارتقا در اسکوپ است، diff لایهچینی استوریج بین نسخهها را ضمیمه کنید.
- initializerها و توابع ارتقا را محافظت کنید. در سازنده پیادهسازی
_disableInitializers()را فراخوانی کنید، مادیفایرهایinitializerوreinitializerرا درست استفاده کنید و_authorizeUpgradeرا برای پراکسیهای UUPS (ERC-1822) محدود کنید. یک قرارداد پیادهسازی نامحافظتشده یافتهای است که ممیزان هرگز نباید مجبور به گزارشش باشند.
۶. یکپارچهسازیهای خارجی#
هر فراخوانی خارجی فرضی درباره کد شخص دیگری است و هر فرض باید نوشته و تست شود.
- کهنگی و خرابی اوراکل را مدیریت کنید.
updatedAtرا با ضربان (heartbeat) فید چک کنید، قیمتهای صفر یا منفی را رد کنید و روی L2ها فید آپتایم سکوئنسر را بررسی کنید. تصمیم بگیرید وقتی اوراکل در دسترس نیست پروتکل چه میکند و آن را مستند کنید. - ویژگیهای عجیب توکن را لحاظ کنید. بگویید کدام انواع توکن پشتیبانی میشوند و توکنهای fee-on-transfer، rebasing، غیر ۱۸ اعشاری و ERC-20 غیراستاندارد را مدیریت یا صراحتاً مستثنی کنید. برای انتقالها از
SafeERC20استفاده کنید و جایی که مبلغ دریافتی مهم است اختلاف بالانس را بسنجید. - سطوح ریانترنسی را نقشهبرداری کنید. هر فراخوانی خارجی و انتقال توکن را فهرست کنید، checks-effects-interactions را دنبال کنید و جایی که وضعیت مشترک است از
ReentrancyGuard(یاReentrancyGuardTransientبا EIP-1153) استفاده کنید. ریانترنسی میانتابعی و فقط-خواندنی و کالبکهای توکنهای ERC-721، ERC-1155 و ERC-777 را هم در نظر بگیرید. - MEV و فرانترانینگ را در نظر بگیرید. به سواپها و واریزها محدودیت اسلیپیج و ددلاین اضافه کنید، والتهای ERC-4626 را در برابر حمله تورمی واریزکننده اول محافظت کنید و هر منطقی که به ترتیب تراکنشها وابسته است را بازبینی کنید. ریسکهای ترتیبی که میپذیرید را مستند کنید.
۷. دیپلوی و عملیات#
کد ممیزیشده فقط بهاندازه روش دیپلوی و بهرهبرداریاش امن است.
- اسکریپتهای دیپلوی را بازتولیدپذیر و پارامترها را مستند کنید. با اسکریپتهای Foundry از commit فریزشده دیپلوی کنید، هر پارامتر سازنده و initializer را در کانفیگ تحت کنترل نسخه نگه دارید و اسکریپتها را در اسکوپ ممیزی یا حداقل در تحویل بگنجانید. یک قرارداد درست که با پارامترهای اشتباه دیپلوی شده هنوز خراب است.
- روی تستنت با سورسهای وریفایشده تمرین کنید. همان اسکریپت دیپلوی را روی یک تستنت و روی یک فورک میننت اجرا کنید، سورسها را روی بلاک اکسپلورر وریفای کنید و بررسی کنید نقشها به آدرسهای موردنظر رسیدهاند. آدرسهای تستنت را به ممیزان بدهید تا با یک دیپلوی واقعی تعامل کنند.
- توقف، رانبوک و مانیتورینگ را آماده کنید. تصمیم بگیرید چه کسی میتواند چه چیزی را متوقف کند، یک رانبوک حادثه کوتاه بنویسید و برای تغییرات نقش، ارتقاها، برداشتهای بزرگ و وضعیتهای متوقفشده هشدار تنظیم کنید. ممیزان مسیرهای اضطراری را بازبینی میکنند، پس باید پیش از ممیزی وجود داشته باشند.
۸. لجستیک ممیزی#
لجستیک خوب ممیزی را جاری نگه میدارد و تضمین میکند دور اصلاح واقعاً استفاده میشود.
- یک نقطه تماس فنی و یک کانال مشخص کنید. یک توسعهدهنده را که کدبیس را میشناسد و میتواند ظرف چند ساعت به سؤالات پاسخ دهد تعیین کنید و برای مدت ممیزی روی یک کانال مشترک توافق کنید. هر سؤال بیپاسخ بازبینی را متوقف میکند.
- برای اصلاحات و یک دور بازبینی اصلاحات وقت رزرو کنید. ظرفیت توسعهدهنده را درست پس از رسیدن گزارش برنامهریزی کنید و از قبل توافق کنید ممیزان اصلاحات را بازبینی کنند. اصلاحات بازبینینشده میتوانند باگهای جدیدی را پس از امضای ممیزی وارد کنند.
- ممیزیها و بازبینیهای قبلی را افشا کنید. گزارشهای ممیزی قبلی، وضعیت اصلاح آنها و هر بازبینی داخلی را به اشتراک بگذارید. ممیزان سپس میتوانند روی آنچه تغییر کرده تمرکز کنند و بررسی کنند یافتههای قبلی عود نکردهاند.
چگونه میتوانیم کمک کنیم#
ما یک اسپرینت آمادگی برای ممیزی با اسکوپ ثابت یک تا دو هفته ارائه میدهیم: یک مدل تهدید، تحلیل شکافهای مجموعه تست، تستهای فاز و اینوریانت Foundry، تریاژ Slither، فهرست اولویتبندیشده اصلاحات و یک بسته تحویل ممیزی شامل اسکوپ، مستندات و مسائل شناختهشده. ما یک شرکت ممیزی نیستیم و اسپرینت جایگزین ممیزی خارجی نیست؛ کد شما را آماده میکند تا ممیزان زمانشان را صرف منطق کنند. جزئیات در صفحه قیمتگذاری و خدمات بلاکچین و Web3 ما آمده است.
اگر تخمین رایگان برای کدبیس خود میخواهید، درباره پروژهتان به ما بگویید. خوشحال میشویم پیش از اشتراک هر کدی یک NDA دوجانبه امضا کنیم.