דלג לתוכן

מאמרים

רשימת בדיקות מוכנות לאודיט של חוזים חכמים

30 בדיקות לפני אודיט של חוזה חכם: היקף ותיעוד, בדיקות, fuzz ו-invariant tests ב-Foundry, מיון Slither, בקרת גישה, אינטגרציות ופריסה.

צוות ההנדסה של sigmacode.io8 דקות קריאה

בעמוד הזה (9)
  1. 1. היקף ותיעוד
  2. 2. היגיינת קוד וניתוח סטטי
  3. 3. בדיקות
  4. 4. בדיקות fuzz ו-invariant
  5. 5. בקרת גישה ו-upgradeability
  6. 6. אינטגרציות חיצוניות
  7. 7. פריסה ותפעול
  8. 8. לוגיסטיקת אודיט
  9. איך אנחנו יכולים לעזור

אודיט של חוזה חכם הוא יקר ומוגבל בזמן באופן נוקשה. ה auditors מקבלים מספר ימים קבוע, וכל שעה שהם מבלים על להבין מה הקוד אמור לעשות, לרדוף אחרי commit שזז או לכתוב את הבדיקות שלא כתבתם — היא שעה שלא מושקעת בלוגיקה שבאמת יכולה לאבד כספים. דוח הממצאים אז מתמלא ב-NatSpec חסר, ב-pragma צפים ובנתיבי revert לא מבוקרים, במקום בבעיות שאתם משלמים כדי למצוא.

קוד מוכן היטב מקבל אודיט עמוק יותר באותו תקציב. רשימת הבדיקות הזו מרכזת את מה שאנחנו מסדירים לפני שמוסרים קוד ל-auditor חיצוני: 30 בדיקות קונקרטיות על פני תיעוד, היגיינת קוד, בדיקות, בקרת גישה, אינטגרציות, פריסה ולוגיסטיקה. היא לא מחליפה את האודיט; היא מוודאה שזמן האודיט הולך למקום שחשוב.

1. היקף ותיעוד#

Auditors יכולים לאמת התנהגות רק מול כוונה שהם מבינים, אז הכוונה חייבת להיות כתובה.

  • הקפיאו hash של commit. תנו ל-auditors commit מתויג אחד ואל תשנו את הקוד שבסקירה במהלך ההתקשרות. תיקונים הולכים ל-branch נפרד ונסקרים בסבב ה-fix-review.
  • פרסמו רשימת קבצי היקף עם nSLOC. רשמו כל חוזה בהיקף עם הנתיב שלו ומספר שורות הקוד המנורמלות (nSLOC), וציינו במפורש מה מחוץ להיקף (ספריות, mocks, סקריפטים, קבצים שעברו אודיט קודם). Auditors מתמחרים ומתכננים לפי nSLOC, אז ספירה מדויקת מונעת הפתעות משני הצדדים.
  • כתבו סקירת ארכיטקטורה. עמוד או שניים שמתארים את החוזים, איך הם קוראים זה לזה, אילו חוזים מחזיקים כספים ומהם זרימות המשתמש העיקריות. דיאגרמה פשוטה של קריאות וזרימות טוקנים חוסכת ל-auditors שעות של reverse engineering.
  • פרטו התנהגות ו-invariants בפרוזה. תארו מה כל פונקציה external חייבת לעשות ואילו תכונות חייבות להחזיק תמיד, למשל "סכום יתרות המשתמשים שווה ל-totalAssets" או "רק ה-timelock יכול לשנות עמלות". ההצהרות האלה הופכות לבסיס גם לסקירת ה-auditors וגם לבדיקות ה-invariant שלכם.
  • תעדו בעיות ידועות וסיכונים מקובלים. רשמו מגבלות שאתם כבר מכירים והחלטות עיצוב שאתם מקבלים, כמו פשרות ריכוזיות או סוגי טוקנים לא נתמכים. זה שומר אותן מחוץ לדוח הממצאים ומראה ל-auditors איפה כבר חשבתם לעומק.

2. היגיינת קוד וניתוח סטטי#

רעש בקוד עולה בזמן אודיט ומפיק ממצאים בעלי ערך נמוך, אז הסירו אותו לפני שה-auditors רואים אותו.

  • נעצו את הקומפיילר והגיעו לאפס אזהרות. השתמשו ב-pragma solidity 0.8.x; מדויק במקום ^0.8.0 צף, וודאו שהגרסה, ה-optimizer runs, ה-via_ir וה-evm_version ב-foundry.toml תואמים למה שתפרסו. כל אזהרת קומפיילר צריכה להיות מתוקנת או מוסברת במודע.
  • נעצו ורשמו תלויות. נעלו את OpenZeppelin Contracts וכל ספרייה אחרת לגרסה מדויקת, למשל submodule מתויג של v5.x או גרסה מדויקת ב-package.json. רשמו את כל התלויות בתיעוד האודיט וסמנו קבצים שהעתקתם או שיניתם.
  • הסירו קוד מת, TODOs ופלט debug. מחקו פונקציות לא בשימוש, קוד מוער, imports של console.log ועוזרי בדיקה שנשארו מהחוזים בהיקף. TODOs פתוחים מסמנים לוגיקה לא גמורה וידווחו ככאלה.
  • השלימו NatSpec על פונקציות external ו-public. לכל פונקציה external ו-public צריך להיות @notice, @param, @return ו, איפה שרלוונטי, הערה על בקרת גישה ו-reverts. השתמשו ב-custom errors ושדרו events לכל שינוי מצב שחשוב מחוץ לשרשרת.
  • הריצו Slither ומיינו כל ממצא. הריצו slither על ה-commit הקפוא וסווגו כל ממצא כמתוקן, false positive או מקובל, עם הצדקה של שורה אחת. כלי שני כמו Aderyn לעיתים קרובות תופס דפוסים אחרים, ושיתוף הפלט הממוין אומר ל-auditors אילו ממצאים אוטומטיים כבר מטופלים.

3. בדיקות#

בדיקות מראות ל-auditors איך הקוד אמור לשמש ונותנות להם לכתוב proofs of concept במהירות.

  • כסו כל פונקציה external ופרסמו את הדוח. כל פונקציה external ו-public צריכה לפחות בדיקה אחת של נתיב השמחה. הפיקו דוח כיסוי שורות וענפים עם forge coverage והסבירו ענפים לא מכוסים במקום להסתיר אותם.
  • בדקו נתיבי revert ומקרי קצה. ודאו שקריאות לא מורשות, פרמטרים לא תקינים, סכומי אפס וערכי גבול מבצעים revert עם ה-custom error הצפוי דרך vm.expectRevert. נתיבי revert לא מבוקרים הם המקום שבו באגי ולידציה ובקרת גישה מסתתרים.
  • הריצו fork tests מול אינטגרציות אמיתיות. אם הפרוטוקול מדבר עם חוזים חיצוניים כמו DEX-ים, שווקי הלוואות או oracles, בדקו מול fork של mainnet או L2 עם מספרי בלוקים נעוצים. Mocks רק מוכיחים שהקוד שלכם עובד עם ההנחות שלכם על הצד השני.

4. בדיקות fuzz ו-invariant#

Fuzzing מוצא את צירופי הקלט שאף אחד לא חשב עליהם, ובדיקות invariant בודקות שהמערכת נשארת עקבית על פני רצפי קריאות.

  • עשו fuzz לכל פונקציה שמקבלת קלט מספרי. כתבו בדיקות fuzz ב-Foundry לסכומים, חותמות זמן, חישובי shares וחשבון עמלות, והגבילו קלטים עם bound() במקום vm.assume מופרז. כיוון עיגול ו-overflow בערכים קיצוניים הם המטרות הטיפוסיות.
  • כתבו בדיקות invariant stateful עם handlers. השתמשו ב-invariant testing של Foundry עם חוזי handler שקוראים למערכת ברצפים ריאליסטיים מכמה שחקנים. בדקו כושר פירעון, שימור ערך ותכונות בקרת גישה אחרי כל רצף קריאות.
  • שמרו את ה-invariants הכתובים ואת סט הבדיקות מסונכרנים. כל invariant מהמפרט צריך להיות ממופה לבדיקת invariant, וכל בדיקת invariant צריכה להיות עקיבה חזרה למפרט. הריצו את הסט עם מספר משמעותי של runs ו-depth ב-CI, לא רק מקומית עם ברירות מחדל.

5. בקרת גישה ו-upgradeability#

פונקציות עם הרשאות ושדרוגים יכולים להזיז או לנעול כל נכס במערכת, אז מודל האמון חייב להיות מפורש.

  • רשמו כל תפקיד ופונקציה עם הרשאות. תעדו כל תפקיד, אילו פונקציות הוא יכול לקרוא, מי מחזיק אותו ומה התרחיש הגרוע ביותר אם המפתח שלו נחשף. זהו סעיף הנחות האמון שה-auditors יבקשו ראשון.
  • שימו כוח ניהולי מאחורי multisig ו-timelock. פרמטרים קריטיים ושדרוגים צריכים להיות בשליטת multisig כמו Safe, עם TimelockController או עיכוב שקול לשינויים שמשפיעים על כספי משתמשים. תעדו את סף החותמים ואת העיכוב.
  • בדקו storage layout לחוזים upgradeable. לפרוקסים, השתמשו בחוזים upgradeable של OpenZeppelin v5 עם namespaced storage של ERC-7201 ואמתו שדרוגים עם ה-upgrades tooling של OpenZeppelin. כללו את ה-diff של ה-storage layout בין גרסאות אם שדרוג בהיקף.
  • הגנו על initializers ופונקציות שדרוג. קראו ל-_disableInitializers() ב-constructor של ה-implementation, השתמשו ב-modifiers initializer ו-reinitializer נכון והגבילו את _authorizeUpgrade לפרוקסי UUPS (ERC-1822). חוזה implementation לא מוגן הוא ממצא ש-auditors לא צריכים לדווח עליו אף פעם.

6. אינטגרציות חיצוניות#

כל קריאה חיצונית היא הנחה על קוד של מישהו אחר, וכל הנחה צריכה להיות כתובה ומבוקרת.

  • טפלו ב-staleness וכישלון של oracle. בדקו את updatedAt מול ה-heartbeat של ה-feed, דחו מחירים אפסיים או שליליים וב-L2-ים בדקו את ה-feed של uptime ה-sequencer. החליטו מה הפרוטוקול עושה כשה-oracle לא זמין ותעדו את זה.
  • התחשבו ב-quirks של טוקנים. ציינו אילו סוגי טוקנים נתמכים וטפלו או שללו במפורש טוקני fee-on-transfer, rebase, לא-18-decimal ו-ERC-20 לא סטנדרטיים. השתמשו ב-SafeERC20 להעברות ומדדו הפרשי יתרה איפה שהסכום המתקבל חשוב.
  • מפו שטחי reentrancy. רשמו כל קריאה חיצונית והעברת טוקנים, עקבו אחר checks-effects-interactions והשתמשו ב-ReentrancyGuard (או ReentrancyGuardTransient עם EIP-1153) איפה שמצב משותף. שקלו reentrancy חוצה-פונקציות ו-read-only, ו-callbacks מטוקני ERC-721, ERC-1155 ו-ERC-777.
  • שקלו MEV ו-front-running. הוסיפו גבולות slippage ו-deadlines ל-swaps והפקדות, הגנו על vaults של ERC-4626 מפני מתקפת האינפלציה של המפקיד הראשון, וסקרו כל לוגיקה שתלויה בסדר טרנזקציות. תעדו אילו סיכוני סדר אתם מקבלים.

7. פריסה ותפעול#

הקוד שעובר אודיט בטוח רק כמו הדרך שבה הוא נפרס ומופעל.

  • עשו סקריפטי deploy ניתנים לשחזור ופרמטרים מתועדים. פרסו עם סקריפטים של Foundry מה-commit הקפוא, שמרו כל פרמטר של constructor ו-initializer בקונפיגורציה בניהול גרסאות וכללו את הסקריפטים בהיקף האודיט או לפחות בהעברה. חוזה נכון שנפרס עם פרמטרים שגויים עדיין שבור.
  • תרגלו על testnet עם מקורות מאומתים. הריצו את סקריפט הפריסה המדויק על testnet ועל fork של mainnet, אמתו את המקורות ב-block explorer ובדקו שהתפקידים הגיעו לכתובות המיועדות. תנו ל-auditors את כתובות ה-testnet כדי שיוכלו לתקשר עם פריסה אמיתית.
  • הכינו השהיה, runbook וניטור. החליטו מי יכול להשהות מה, כתבו runbook קצר לתקלות והקימו התראות על שינויי תפקידים, שדרוגים, משיכות גדולות ומצבי השהיה. ה-auditors יסקרו את נתיבי החירום, אז הם צריכים להתקיים לפני האודיט.

8. לוגיסטיקת אודיט#

לוגיסטיקה טובה שומרת על האודיט בתנועה ומוודאה שסבב התיקונים באמת מנוצל.

  • מנו איש קשר טכני וערוץ. הקצו מפתח אחד שמכיר את הקוד ויכול לענות על שאלות בתוך שעות, וסכמו על ערוץ משותף למשך האודיט. כל שאלה שלא נענית מעכבת את הסקירה.
  • שמרו זמן לתיקונים ולסבב fix-review. תכננו קיבולת פיתוח מיד אחרי שהדוח מגיע וסכמו מראש שה-auditors סוקרים את התיקונים. תיקונים לא נסקרים יכולים להכניס באגים חדשים אחרי שהאודיט אישר.
  • גלו אודיטים וסקירות קודמות. שתפו דוחות אודיט קודמים, סטטוס התיקונים שלהם וכל סקירה פנימית. ה-auditors יכולים אז להתמקד במה שהשתנה ולבדוק שממצאים קודמים לא חזרו.

איך אנחנו יכולים לעזור#

אנחנו מציעים Audit-Readiness Sprint בהיקף קבוע של שבוע עד שבועיים: מודל איומים, ניתוח פערים בסט הבדיקות, בדיקות fuzz ו-invariant ב-Foundry, מיון Slither, רשימת תיקונים מסודרת לפי עדיפות וחבילת העברה לאודיט עם היקף, תיעוד ובעיות ידועות. אנחנו לא חברת אודיט והספרינט לא מחליף אודיט חיצוני; הוא מכין את הקוד שלכם כדי שה-auditors יוכלו להשקיע את זמנם בלוגיקה. הפרטים בעמוד התמחור שלנו ובשירותי Blockchain & Web3.

אם תרצו הערכה חינם לקוד שלכם, ספרו לנו על הפרויקט שלכם. נשמח לחתום על NDA הדדי לפני שאתם משתפים קוד.

יש פרויקט בראש?

ספר לנו מה אתה בונה. מהנדס בכיר משיב תוך שעה בימי עבודה — ואז אתה מקבל הערכה כנה, היקף ברור והצעה במחיר קבוע או מבוסס אבני דרך, בדרך כלל תוך כמה ימי עבודה.

מעדיף לכתוב קודם? כתוב לנו

תדבר ישירות עם Ing. Ismet Mesic, Tech lead.