פיתוח סקריפטים למיגרציה של חוזים חכמים

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

שירותי פיתוח בלוקצ'יין

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1376
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1350
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    798
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1088
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1034
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1130

מדוע מיגרציה של חוזים חכמים נשברת בסביבת הייצור

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

אם סדר השדות והטיפוסים ב-slots אינם תואמים, הקוד החדש קורא ערכים זרים כשל עצמו: היתרות הרשומות מפסיקות להתאים לכספים המוחזקים בפועל, והקורא רואה אפסים במקום שבו מונח הכסף.

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

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

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

השורש המשותף לכל שלושת הכשלים במיגרציה של חוזים חכמים הוא אחד: את ההעברה בודקים על מצב ריק, לא על מצב ייצור. לכן אנו מפרטים כיצד זה נסגר — הרצה על עותק של מצב הייצור, אינווריאנטים «לפני/אחרי» ומעבר מדורג.

מה נותנת מיגרציה מנוהלת: אפקטים מדידים

צמצום חלון אי-הזמינות בעת המעבר

ההעברה נבנית כך שהמעגל החדש עולה לצד הישן: המצב מועתק מראש, הלוגיקה מתחממת על עותק של הרשת הראשית, והמעבר מצטמצם להחלפת כתובת המימוש בתבנית ה-proxy של OpenZeppelin. לא נותרים שלבים ידניים ברגע המעבר — אותם מבצעים בשלב ההכנה.

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

סוג הכשלים שנעלם לאחר ההעברה

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

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

גישות להעברת מצב: השוואת מצבים

העברת הלוגיקה היא חצי מהעבודה; קשה יותר להחליט מה לעשות עם המצב: יתרות, הרשאות חיוב, תפקידי גישה והפניות לאובייקטים שנוצרו קודם. בפועל בוחרים באחד משלושה מצבים, וכל אחד מגביל בדרכו את יכולת הבדיקה של השינויים.

אפשרות מתי מתאימה מגבלות
Proxy מתעדכן הכתובת וה-storage חייבים להישאר כשהיו פריסת ה-slots קבועה, שגיאה בה שוברת את קריאת המצב
העברת מצב מלאה מודל הנתונים או הארכיטקטורה משתנים יקר במספר הטרנזקציות, נדרשים snapshot ואימות חוזר
מעבר מדורג: החוזה הישן לקריאה בלבד המשתמשים נודדים בקצב שלהם שני מקורות אמת חיים זמן רב

Proxy מתעדכן

את הלוגיקה מוציאים אל מחוץ ל-proxy, הכתובת וה-storage נשארים כשהיו. העיקר — פריסת ה-slots: את הגרסה החדשה בונים עם אותו סדר משתנים, משאירים רזרבה לשדות עתידיים ולא משנים טיפוסים, אחרת המצב נקרא כזבל.

לפני ה-deploy חובה בדיקה אוטומטית של סטיית סכמות: את השלד מספק OpenZeppelin, אך את אינווריאנטים ה-storage מתארת הצוות.

העברת מצב מלאה — כששום דרך אחרת לא אפשרית

מודל הנתונים או הארכיטקטורה משתנים — ה-proxy לא מציל: מצלמים snapshot, מעבירים רשומות בקבוצות ופותחים את החוזה הישן לקריאה בלבד. כל קבוצה נבדקת לפי סכום ביקורת של העץ לפני ואחרי, שיעור הרשומות שהועברו ודוח סטיות. כל עוד החוזה הישן חי, קיימים שני מקורות אמת — סיכון זה נסגר בלוח זמנים, לא בהבטחה.

שלבי העבודה: מביקורת עד מסירת נהלים

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

  1. ביקורת קוד: מפרקים את החוזים, הקשרים ביניהם, הרשאות התפקידים והמצבים המצטברים.
  2. תרשים ההעברה: מסמנים מה עובר כפי שהוא, מה מחושב מחדש, ומה נשאר בחוזה הקודם לקריאה בלבד.
  3. בניית סקריפטים למיגרציה: הקריאות נעשות ניתנות לחזרה — הרצה חוזרת לא יוצרת כפילויות ולא פוגעת ביתרות.
  4. חזרה כללית על עותק של הרשת: מחזור מלא בסביבת בדיקה עם אותם היקפי רשומות כמו במעגל הראשי.
  5. בדיקת ההתנהגות: משווים סכומים, יתרות והרשאות לפני ואחרי, ועוברים בנפרד על תרחיש הנסיגה.
  6. המעבר: מחליפים כתובות באינטגרציות, ומעבירים את החוזה הישן למצב קריאה.
  7. מסירת נהלים: הוראות תפעול, סדר פעולות בעת כשל, מטריצת אחריות.

סקר מלאי

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

חזרה כללית על עותק של הרשת

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

מה נשאר אצל הלקוח: תרשימים, מדיניות, נהלים

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

הרכב התיעוד

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

נוהל גישה ומפתחות

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

מקרה בוחן: העברת שירות לסכמה חדשה בלי עצירה

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

המצב ההתחלתי

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

התוצאה לפי מדדים

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

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

מה לעשות אם הקוד כבר פרוס ומחזיק כספים

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

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

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

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

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

ספקות טכניים לפני תחילת העבודה

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

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

מה יקרה לכספי המשתמשים?

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

האם הלוגיקה הישנה תואמת לחדשה?

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

כיצד להוכיח שההתנהגות זהה?

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

האם יש צורך לעצור את השירות?

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

מה לעשות אם החישובים ייפרדו?

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

נדון במשימה: ביקורת, תרשים ותוכנית העברה

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

  • כתובות החוזים והרשתות, כולל סביבות בדיקה שבהן נבדקו הגרסאות.
  • קוד המקור ואישור שהם תואמים ל-bytecode ברשת: ארטיפקטי בנייה, פרמטרי מהדר, הגדרות אופטימיזציה.
  • תיאור המצב המאוחסן: אילו מבנים מחזיקים יתרות, תפקידים, רשימות ומגבלות, ומה מזה חייב לעבור.
  • תרשים הגישות: היכן מפתחות המנהל, האם יש multi-sig ו-timelock, מי חותם על טרנזקציות.
  • תלויות חיצוניות: oracles, pools, חוזים זרים שקוראים לשלכם או שאתם קוראים להם.
  • דרישות להעברה: האם שומרים על הכתובות או עוברים ל-proxy חדש, האם חלון השבתה מותר.
  • מגבלות: מגבלות גודל החוזה ו-gas לטרנזקציה אחת, סדר חימום ה-storage.
  • היסטוריית בדיקות: דוחות ביקורת, פגמים ידועים, אירועי עבר והניתוחים שלהם.

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