פיתוח סקריפטים לפריסת חוזים חכמים כשירות מלא

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

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

שאלות נפוצות

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

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

פריסת חוזים חכמים: היכן בדיוק התהליך נשבר

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

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

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

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

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

מה נותן שחרור חוזים מנוהל

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

זמן שחרור החוזה נמדד בדקות

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

פריסה ניתנת לשחזור במקום "איך שיצא"

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

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

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

אפשרויות פריסת חוזים: השוואת גישות

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

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

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

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

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

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

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

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

  3. הגדרת הרשאות ומפתחות. תפקידים: שחרור, ניהול, השהיה. המפתחות יוצאים מארנקים אישיים למאגר מנוהל, שבו נדרש סף אישורים.

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

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

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

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

מה נשאר אצלכם: קונפיגורציות, סכמות, נהלים

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

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

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

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

לפני: רשימת בדיקה ידנית בכל שחרור

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

אחרי: בנייה, חתימה ויומן בתרחיש אחד

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

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

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

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

במה שחרור ברשת טסט שונה מהרשת הראשית?

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

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

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

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

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

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

  • אימות קוד מקור והתאמה של הקוד שנפרס;
  • סכמת תפקידים והעברת בעלות ל-multisig;
  • חלון עיכוב לפונקציות ניהול;
  • תרחיש השהיה וסדר הגירת מצב.

ספקות טכניים בשחרור חוזים

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

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

מי מחזיק במפתחות עד למסירה?

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

כיצד לוודא שהקוד שנפרס תואם לזה שנמסר?

משווים hash של ה-bytecode בכתובת ל-bytecode מהבנייה שנמסרה, ומאמתים את קוד המקור בסייר הבלוקים, כדי שכל אחד יוכל להשוות את הקוד. במקביל משווים ABI: חתימות המתודות והאירועים צריכים להיות תואמים עד לתו. אי-התאמה אפילו במקום אחד — עצירה עד לבירור הסיבות.

מה עם האחריות אחרי המסירה?

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

נדון במשימה: ממה מתחילה העבודה

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

  • רשתות יעד: ראשית או טסט, אחת או כמה, האם נדרשת העברת לוגיקה ביניהן.
  • לוגיקת החוזים: טוקן עם תקן, staking, multisig, סכמת proxy לעדכונים עתידיים.
  • מודל גישה: מי הבעלים, האם נדרשים multisig ו-timelock לשינוי פרמטרים קריטיים.
  • סדר הפריסה: רצף השחרור, אתחול פרמטרים, קשרים בין החוזים, חזרתיות הסקריפטים בהרצה מחדש.
  • מפתחות וחתימות: היכן נשמרים, מי חותם, כיצד מופרדים התפקידים בתוך צוות הלקוח.
  • ניתנות לבדיקה: אימות קוד מקור בסייר הרשת, פרסום כתובות וחפצי בנייה.
  • תמיכה: ניטור אירועים, התראות על קריאות קריטיות, תרחיש השהיה או rollback.

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