מדוע סביבת הבדיקות של חוזים חכמים נשברת לפני השקת הגרסה
הרצה מקומית ירוקה, אך העסקה הראשונה בפריסה נופלת עם revert. הסיבה כמעט לעולם אינה בלוגיקה של החוזה, אלא בפערים בין הסביבות: סביבת הבדיקות של החוזים נשענת על תמונת מצב מוצמדת של הרשת, שהספיקה לסטות מהמצב בפרודקשן — כתובות תלויות שונות, יתרות, תפקידים שהוקצו.
השכבה השנייה היא הזמן וסדר הקריאות. בבדיקות מזיזים את חותמת הזמן ידנית כדי להגיע לענף הלוגיקה הרצוי, אבל בפרודקשן היא אמיתית — העסקה נופלת לחלון שבו התנאי עוד לא מתקיים. Nonce ותור החתימה הרב-חתמית מוסיפים נפילות של עסקאות בפריסה במקומות שבהם מקומית פשוט הרצנו את השלב בסדר אחר.
מכאן גם הבדיקות הלא-יציבות: חלקן פונות לצמתים חיצוניים, נתקלות במגבלות קצב בקשות ובניתוקים, ותמונת מצב לא מוצמדת של הרשת מניבה תוצאה שונה בין הרצה להרצה.
בנוסף, שלבים ידניים — משתני סביבה, מפתחות של הפורס, הקצאת הרשאות דרך הקונסול: מחציתם אינם משוחזרים בפרודקשן, ואת השלב שהוחמץ אפשר למצוא רק בהשוואת סביבות.
אנחנו מסבירים כיצד סוגרים את זה: סביבת בדיקות משוחזרת לחוזים, תמונות מצב מוצמדות של הרשת, תלויות חיצוניות מבודדות והעברת שלבים ידניים לסקריפטים של פריסה. המטרה אינה שיהיו יותר בדיקות, אלא שתוצאת ההרצה תתאים להתנהגות ברשת הפרודקשן — אז נפילה בפריסה הופכת לאירוע נדיר ולא לנורמה.
מה משתנה אחרי ההטמעה: מהירות הבדיקות וסוגי הכשלים
לפני קו הבדיקות הייעודי, ההרצות התבצעו על רשת בדיקות משותפת: מצב החשבונות והאחסון השתנה בין הרצה להרצה, וחלק מהנפילות לא שוחזרו במחשב של המפתח. כל מקרה כזה — שעה של ניתוח ידני והרצה חוזרת בעיוורון.
צומת מקומי מדמה עם תמונת מצב מוצמדת מסיר את המשתנה הזה. ההרצה מתחילה מאותו בלוק ומאותה קבוצת נתונים, ולכן הנפילה חוזרת על פי תמונת המצב — שיעור הנפילות המשוחזרות שואף לשלם, ולא למחצית מהמקרים.
את המהירות נותנת השילוב של מטמון קומפילציה והרצה מקבילה של בדיקות בלתי תלויות ב-Foundry. מערך שבעבר ארך עשרות דקות והורץ פעם ביום, מתכנס לדקות ויוצא על כל שינוי — הרגרסיה נתפסת לפני המיזוג לענף הראשי, ולא אחרי העלייה לאוויר.
סוג נפרד של כשלים נסגר באמצעות תיאור סביבה אחיד: גרסאות תלויות, יתרות התחלתיות, צעד הזמן והרשאות תפקידים מוגדרים בקונפיגורציה ונמצאים במאגר הקוד לצד החוזה. הפער „עובד מקומית, לא עובד בסטנד” מפסיק להיות סיפור נפרד.
ההכנה לשחרור מתקצרת בזכות בדיקה על אותה תמונת מצב: לפני העלייה לאוויר מתבצעת הרצה אחת עם דוח כיסוי ורשימת החוזים שהשתנו. נפילה עוצרת את השחרור, וחזרה לתמונת המצב הקודמת מחזירה את הסטנד למצב מוכר ללא עריכה ידנית של נתונים.
אפשרויות הרצה: רשת מקומית, פורק של הרשת הראשית, סטנד משותף
סביבת ההרצה קובעת מה אפשר לתפוס לפני הפריסה לרשת, ולכן בוחרים את מצב הסטנד לפי המשימה, ולא מתוך הרגל. רשת מקומית עולה על מכונה אחת: המצב מתאפס להתחלה, וההרצה משוחזרת אחד לאחד.
פורק של הרשת הראשית קורא את מצב החוזים הזרים בגובה בלוק מוצמד — כך בודקים אינטגרציות בלי לפרוס סביבה ידנית.
תשתית הקומפילציה והדיבוג מוגדרת לפי המצב הנבחר: מכך תלוי כיצד ייראה שחזור של הרצה שנפלה ועד כמה מלא יהיה דוח כיסוי המסלולים.
| מצב | מתי מתאים | מגבלות |
|---|---|---|
| רשת מקומית | הרצת לוגיקה, בדיקות יחידה, דיבוג של חוזה בודד | אין חוזים חיצוניים; המצב בזיכרון אינו שורד אתחול מחדש |
| פורק של הרשת הראשית | אינטגרציות עם פרוטוקולים זרים על נתונים אמיתיים | תלוי בצומת חיצוני; יש להצמיד את גובה הבלוק, אחרת ההרצות נבדלות זו מזו |
| סטנד משותף לצוותים | קבלה, הדגמה, עבודה במקביל של כמה מפתחים וקבלנים | נדרש נוהל גישה ואיפוס מצב: טעות של משתתף אחד שוברת את ההרצות של האחרים |
בידוד הסביבות הוא מה שמבדיל בין המצבים בפועל. סטנד משותף לצוותים חי זמן רב, ולכן נשען על נוהל: ענף נפרד לכל משימה, תמונת מצב לפני קבלה, חזרה אוטומטית למצב נקי לפי לוח זמנים. בלי זה המצב „שט” בין הרצות, ובדיקה שנופלת מפסיקה להוכיח משהו.
כיצד בוחרים מצב
ב-Foundry אנחנו בונים מחזור מקומי מהיר והרצות פורק, ואת הסטנד המשותף מייעדים לתרחישים ארוכים ולהדגמה ללקוח. בפועל משלבים בין המצבים: רשת מקומית — על כל שינוי, פורק של הרשת הראשית — לפני מיזוג, סטנד — בקבלה.
כיצד מתבצעת ההטמעה: מביקורת הקוד ועד מסירת הסטנד
את סדר העבודה בונים כך שסביבת הבדיקות של החוזים החכמים תקום לפני העסקה הראשונה בפרודקשן, ולא אחרי תקלה. תחילה בוחנים מה כבר קיים בפרויקט, לאחר מכן מתכננים סביבות, ורק אז כותבים בדיקות — אחרת הבדיקות מצמידות את ההתנהגות הנוכחית יחד עם שגיאותיה.
- ביקורת הסביבה. משווים גרסאות קומפיילר ותלויות, בודקים היכן מאוחסנים המפתחות וכתובות הרשתות, אילו בדיקות כבר קיימות ואילו ענפי לוגיקה אינם מכוסים. בנפרד מסמנים שלבים שאינם משוחזרים על מכונה נקייה.
- תרשים הסטנדים. מגדירים מערך סביבות: צומת מקומי להרצות מהירות, רשת מבודדת לאינטגרציה, פורק של הרשת הראשית — על מצב אמיתי של החוזים. לכל אחת משתנים, מפתחות וגבולות גישה משלה.
- הגדרת הסביבה. מאחדים את הקונפיגורציה של Foundry ו-Hardhat לפרופילים עם תמונת מצב דטרמיניסטית: כל הרצה מתחילה מאותה נקודה, והנפילה משוחזרת.
- חיבור הבדיקות. בדיקות יחידה על אינוריאנטים, פאזינג של ערכי גבול, בדיקות תפקידים וקריאות חוזרות. את ההרצה משבצים בקו הבנייה: בלי בדיקות ירוקות הקוד לא ממשיך הלאה.
- בדיקה על תרחישים אמיתיים. משחזרים על הפורק רצפים מייצור — הנפקה, העברה, הגירת מצב, כשלים של קריאות חיצוניות — ומשווים את צריכת הגז לזו הצפויה.
- נוהל ההרצה. קובעים אילו בדיקות חובה לפני שחרור, מי מריץ אותן וכיצד מנוסחת החזרה לאחור.
- מסירת הסטנד. מוסרים מאגר קוד עם קונפיגורציה, תיאור סביבות והרשאות — הצוות מנהל את הסביבה בעצמו.
מה נמסר לידיים: קונפיגורציות, תרשימים ונוהלים
איננו מוסרים תיקייה עם בדיקות, אלא סטנד משוחזר: הוא נפרס בפקודה אחת ומניב אותה תוצאה מקומית, בסביבה משותפת ובקו האינטגרציה הרציפה.
- קונפיגורציות הסטנד — פרופילים של Foundry ו-Hardhat, הגדרות הצומת המקומי: זמן בלוק, מגבלות גז, יתרות התחלתיות, הצמדת הפורק של הרשת הראשית לבלוק מסוים.
- תרשימי הסביבות — מפת הקווים: היכן יושבים החוזים, אילו שירותים חיצוניים הוחלפו בסטאבים, היכן מחוברות כתובות אמיתיות, וכיצד הקווים מבודדים זה מזה.
- מדיניות גישה — תפקידים לפריסה, עדכון פרוקסי ושינוי פרמטרים של הפרוטוקול; המפתחות נשמרים במאגר מאובטח ואינם נכנסים למאגר הקוד.
- מערכי נתוני בדיקה — מצבים לתרחישים: מאגר נזילות, פוזיציות של משתמשים, סכומי גבול, כתובות עם יתרה אפסית ומקסימלית.
- תיעוד הסטנד — סדר ההרצה, מפת כיסוי: אילו מסלולים מכוסים בבדיקות יחידה, אילו בתרחישים על הפורק, ואילו אינוריאנטים נבדקים בנפרד.
- נוהלי מסירה — כיצד לעדכן בדיקות כשהחוזים משתנים, מי מוסיף בדיקות חדשות, ובאיזה שלב ההרצה חוסמת את הבנייה.
הכול נמצא במאגר הקוד לצד הקוד ומנוהל בגרסאות יחד איתו. מהנדס חדש נכנס לפרויקט בלי שיספרו לו מחדש „איך אצלנו נהוג”.
מקרה בוחן: סטנד לפרוטוקול סליקה — מדדים לפני ואחרי
פרוטוקול סליקה עם כמה חוזים קשורים נבדק על עותק של הרשת הראשית, אך התרחישים נבנו ידנית. הרצה מלאה ארכה כשעתיים, חלק מההרצות בוטלו, וחלק מהנפילות צפו רק אחרי הפריסה — על היתרות האמיתיות. כאשר נבנה סטנד על Foundry, תיעדנו את המדדים לפני ואחרי.
מה הפריע לפני
הסביבה הורמה ידנית: מוני כתובות, תפקידים, מגבלות, יתרות התחלתיות. מתוך עשר נפילות שוחזרו שלוש — השאר היו תלויות במצב המצטבר של העותק, והזמן הושקע בדיבוג התרחיש ולא בתיקון החוזה. הפריסה כללה אחד עשר שלבים ידניים: הגירות, הרשאות, פרמטרים, השוואת כתובות. פעם אחת הוחמץ שלב של הקצאת תפקיד — הסליקה נעצרה עד להתערבות ידנית.
מה קרה אחרי
הסטנד מרים את אותו מצב בסקריפט חוזר: מערך החשבונות, המגבלות והיתרות זהה בין הרצה להרצה. ההרצה התקצרה משעתיים לשבע דקות, שחזור הנפילות עלה משלושה לעשרה מקרים מתוך עשרה, ובפריסה נותר שלב אחד — אישור. הרגרסיה נתפסת לפני מיזוג השינויים, ולא אחרי השחרור.
| מדד | לפני | אחרי |
|---|---|---|
| הרצה מלאה של בדיקות | ~שעתיים | ~7 דקות |
| נפילות משוחזרות מתוך עשר | 3 | 10 |
| שלבים ידניים בפריסה | 11 | 1 |
| כיסוי תרחישי הסליקה | סלקטיבי | כל ענפי הסליקה |
| חזרה למצב קודם | לפי צ'קליסט | בפקודה אחת |
מה עושים אם החוזים החכמים כבר פרוסים ברשת הראשית?
חוזים חכמים פרוסים אינם מפריעים לדיבוג — מה שמפריע הוא היעדר סטנד שמשחזר את מצבם. צומת מקומי במצב פורק עולה מעל הרשת הראשית: הוא קורא קוד ונתונים לפי מספר בלוק מסוים, ולאחר מכן מתנהג כסביבת בדיקות רגילה לחוזים חכמים.
אין צורך לשכתב את הלוגיקה: היתרות, התפקידים ומבני האחסון נשארים בדיוק כפי שהיו ברשת ברגע תמונת המצב.
פרט מפתח — תמונת מצב הרשת נלקחת לפי בלוק קבוע, ולא לפי האחרון. אחרת השחזוריות אובדת בין הרצות: משתנות היתרות במאגרים, תשובות האורקלים, מערך המשתתפים הפעילים — והבדיקה נופלת לא בגלל שגיאה בקוד. הצמדת הבלוק הופכת את ההרצה לדטרמיניסטית, והתקלה מנותחת על אותו מצב שבו התרחשה.
העברת הבדיקות מתבצעת בלי לשנות את החוזים עצמם: הבדיקות פונות לכתובות שכבר פרוסות דרך ממשקים, ולא דרך פריסה מחדש. אנחנו מוסיפים רק את התשתית — הכנת חשבונות, הקצאת תפקידים, קריאות בשם משתתפים אמיתיים.
כך נבדקים תרחישים שאינם משוחזרים על סטנד ריק: פרוטוקולים חיצוניים, מגבלות מצטברות, היסטוריית פעולות.
עדכון חוזים נבדק באותו מקום. עבור סכמות פרוקסי, המימוש החדש מורץ מול תמונת המצב: פריסת האחסון לפי סלוטים, אתחול, התנהגות המתודות הישנות. הסטה של משתנה תוצג על ידי הסטנד לפני שהשחתת הנתונים תעבור לרשת.
ספקות טכניים: בידוד, נתונים, שחזוריות
הסטנד עולה כסביבה נפרדת: רשת מקומית משלו, מפתחות בדיקה משלו, תמונת מצב משלו של הרשת הראשית. את הנתונים לוקחים בשני מסלולים — תמונת מצב במספר בלוק מוצמד, כשצריך יתרות אמיתיות והתנהגות של חוזים שכבר פרוסים, או חשבונות סינתטיים שהסקריפט של הפריסה יוצר. מפתחות וכתובות של הרשת הראשית אינם נכנסים לסביבה, ולכן אי אפשר פיזית לפגוע במצב הייצור בהרצת בדיקה.
החזרתיות נשענת על הצמדת תנאי הקלט: גרסת הקומפיילר, הגדרות האופטימיזטור, תלויות מוצמדות ומספר בלוק של תמונת המצב נמצאים במאגר הקוד לצד הבדיקות. כל מהנדס מרים את אותה סביבה בפקודה אחת, וההרצה משוחזרת בבדיקה אוטומטית בכל שינוי — הפער „אצלי זה עובר, אצלך לא” מפסיק להיות תרחיש עבודה.
עדכון הסטנד והענקת גישה לצוות הם חלק מהתחזוקה, ולא הגדרה חד-פעמית. אנחנו מתארים נוהל: באיזו תדירות מתעדכנת תמונת המצב, מי מוסיף חשבונות בדיקה, כיצד מוענקת גישה ומה קורה בעת החלפת גרסת כלים.
עד כמה הסטנד מבודד מרשת הייצור?
באופן מלא. לסביבה אין גישה למפתחות הייצור והיא חותמת עסקאות רק במפתחות בדיקה. גם אם תהיה שגיאה בסקריפט הפריסה, השינויים יישארו בתוך הרשת המקומית, ותמונת המצב מתעדכנת ידנית.
מפתח אחר ישחזר את ההרצה שלי?
כן, אם התלויות, גרסת הקומפיילר ומספר בלוק תמונת המצב מוצמדים. כל תנאי הקלט נשמרים במאגר הקוד, ולכן הסביבה נבנית בפקודה אחת ומניבה תוצאה זהה בכל מכונה.
מי מתחזק את הסביבה בהמשך?
אנחנו מוסרים קונפיגורציות, נוהל עדכון תמונת המצב והוראות לצוות. תחזוקה היא הסדר נפרד: מעדכנים גרסאות כלים, מוסיפים תרחישים ודואגים שהסטנד ייבנה אחרי שינויים בחוזים.
נדון במשימה ונחזור עם תוכנית עבודה
כדי לחזור עם הערכת היקף ולא עם מילים כלליות, נדרש קלט קצר: כיצד בנוי מחזור השחרור שלכם כעת והיכן הוא נכשל. מספיק לתאר את המשימה בצורה חופשית — את השאר נדייק בשאלות ונראה אילו בדיקות יסגרו סיכונים ספציפיים.
- הרכב החוזים: אילו מודולים בפרודקשן, מה קשור זה לזה, היכן ספריות משותפות וחלקים מתעדכנים.
- קו הבדיקות הנוכחי: אילו בדיקות כבר קיימות, על מה הן רצות, מה עדיין נבדק ידנית לפני העלייה לאוויר.
- תפקידי הצוותים: מי כותב חוזים, מי עושה רוויו, מי פורס ולמי נדרשת גישה לסביבות הפרוסות.
- הרשאות גישה: מאגר הקוד, קונפיגורציית הבנייה והפריסה, רשתות בדיקה, הרשאות לפריסה.
- צווארי בקבוק בשחרור: היכן נשבר לרוב, כמה זמן אורכים הרצת הבדיקות והחזרה לאחור, אילו בדיקות מדולגות תחת לוחות זמנים.
- דיווח: האם נדרשים מדדי כיסוי, דוחות צריכת גז ועקבות בדיקה פורמליים לביקורת חיצונית.
בהמשך נרכיב סטנד בדיקות מבודד, המחובר לקו שלכם, ונקבע את סדר ההתחלה: שלבים, היקף העבודה ונקודות שבהן אתם רואים תוצאה. תארו את המשימה — נחזור עם הערכת היקף ותוכנית עבודה.







