Оновлення смарт-контрактів
Останні оновлення сторінки: 21 жовтня 2025 р.
Смарт-контракти на Ethereum — це самовиконувані програми, які працюють у віртуальній машині Ethereum (EVM). Ці програми незмінні за дизайном, що запобігає будь-яким оновленням бізнес-логіки після розгортання контракту.
Хоча незмінність необхідна для надійності, децентралізації та безпеки смарт-контрактів, у певних випадках така властивість може бути недоліком. Наприклад, незмінний код зробити неможливим для розробників виправлення вразливих контрактів.
Однак посилення досліджень удосконалення смарт-контрактів призвело до впровадження кількох шаблонів оновлення. Ці шаблони оновлення дозволяють розробникам оновлювати смарт-контракти (зберігаючи незмінність), розміщуючи бізнес-логіку в різних контрактах.
Передумови
Вам слід добре розбиратися в смарт-контрактах, анатомії смарт-контрактів і віртуальній машині Ethereum (EVM). Цей посібник також передбачає, що читачі мають знання про програмування смарт-контрактів.
Що таке оновлення смарт-контракту?
Оновлення смарт-контракту передбачає зміну бізнес-логіки смарт-контракту зі збереженням стану контракту. Важливо розуміти, що можливість оновлення та змінність – це не одне й те саме, особливо в контексті смарт-контрактів.
Ви все ще не можете змінити програму, розгорнуту за адресою в мережі Ethereum. Але ви можете змінити код, який виконується, коли користувачі взаємодіють зі смарт-контрактом.
Це можна зробити використовуючи наступні методи:
-
Створення кількох версій смарт-контракту та перенесення стану (тобто даних) з старого контракту в новий екземпляр контракту.
-
Створення окремих контрактів для зберігання бізнес-логіки та стану.
-
Використання шаблонів проксі для делегування викликів функцій від незмінного проксі-контракту до модифікованого логічного контракту.
-
Створення незмінного основного контракту, який взаємодіє та покладається на гнучкі супутникові контракти для виконання певних функцій.
-
Використання алмазного шаблону для делегування викликів функцій від проксі-контракту до логічних контрактів.
Механізм оновлення № 1: міграція контракту
Міграція контракту базується на керуванні версіями — ідеї створення та керування унікальними станами одного й того ж програмного забезпечення. Міграція контракту передбачає розгортання нового екземпляра наявного смарт-контракту і перенесення сховища та балансів до нового контракту.
Щойно розгорнутий контракт матиме пусте сховище, що дозволить вам відновити дані зі старого контракту і записати їх у нову реалізацію. Після цього вам потрібно буде оновити всі контракти, які взаємодіяли зі старим контрактом, щоб вони вказували на нову адресу.
Останній крок у міграції контракту — переконати користувачів перейти на використання нового контракту. Нова версія контракту збереже баланси та адреси користувачів, що зберігає незмінність. Якщо це контракт на основі токенів, вам також потрібно буде зв'язатися з біржами, щоб відмовитися від старого контракту та використовувати новий.
Міграція контракту є відносно простим і безпечним заходом для оновлення смарт-контрактів без порушення взаємодії з користувачами. Однак ручна міграція сховища та балансів користувачів у новий контракт потребує багато часу і може призвести до високих витрат на газ.
Докладніше про міграцію контрактів. (opens in a new tab)
Механізм оновлення № 2: розділення даних
Інший метод оновлення смарт-контрактів полягає в тому, щоб розділити бізнес-логіку та сховище даних на окремі контракти. Це означає, що користувачі взаємодіють із логічним контрактом, тоді як дані зберігаються в контракті сховища.
Логічний контракт містить код, який виконується, коли користувачі взаємодіють із застосунком. Він також містить адресу контракту сховища та взаємодіє з ним для отримання та встановлення даних.
Водночас контракт сховища зберігає стан, пов'язаний зі смарт-контрактом, наприклад баланси та адреси користувачів. Зауважте, що контракт сховища належить логічному контракту і під час розгортання конфігурується з адресою останнього. Це запобігає виклику контракту сховища неавторизованими контрактами або оновленню його даних.
За замовчуванням контракт сховища є незмінним, але ви можете замінити логічний контракт, на який він посилається, новою реалізацією. Це змінить код, який виконується в EVM, зберігаючи при цьому сховище та баланси недоторканими.
Використання цього методу оновлення вимагає оновлення адреси логічного контракту в контракті сховища. Ви також повинні налаштувати новий логічний контракт з адресою контракту сховища з причин, пояснених раніше.
Шаблон розділення даних, ймовірно, легше реалізувати порівняно з міграцією контракту. Однак вам доведеться керувати кількома контрактами та впроваджувати складні схеми авторизації, щоб захистити смарт-контракти від зловмисних оновлень.
Механізм оновлення № 3: шаблони проксі
Шаблон проксі також використовує розділення даних, щоб зберігати бізнес-логіку та дані в окремих контрактах. Однак у шаблоні проксі контракт сховища (який називається проксі) викликає логічний контракт під час виконання коду. Це протилежність методу розділення даних, де логічний контракт викликає контракт сховища.
Ось що відбувається в шаблоні проксі:
-
Користувачі взаємодіють із проксі-контрактом, який зберігає дані, але не містить бізнес-логіки.
-
Проксі-контракт зберігає адресу логічного контракту і делегує всі виклики функцій логічному контракту (який містить бізнес-логіку) за допомогою функції
delegatecall. -
Після того, як виклик перенаправлено до логічного контракту, повернені дані з логічного контракту вилучаються і повертаються користувачеві.
Використання шаблонів проксі вимагає розуміння функції delegatecall. По суті, delegatecall — це код операції, який дозволяє одному контракту викликати інший, тоді як фактичне виконання коду відбувається в контексті контракту, що викликає. Наслідком використання delegatecall у шаблонах проксі є те, що проксі-контракт читає та записує у своє сховище і виконує логіку, що зберігається в логічному контракті, ніби викликаючи внутрішню функцію.
З документації Solidity (opens in a new tab):
Існує спеціальний варіант виклику повідомлення під назвою delegatecall, який ідентичний виклику повідомлення, за винятком того, що код за цільовою адресою виконується в контексті (тобто за адресою) контракту, що викликає, а
msg.senderіmsg.valueне змінюють своїх значень. Це означає, що контракт може динамічно завантажувати код з іншої адреси під час виконання. Сховище, поточна адреса та баланс, як і раніше, належать до контракту, що викликає, лише код береться з адреси, що викликається._
Проксі-контракт знає, що потрібно викликати delegatecall щоразу, коли користувач викликає функцію, оскільки в нього вбудована функція fallback. У програмуванні на Solidity резервна функція (opens in a new tab) виконується, коли виклик функції не збігається з функціями, зазначеними в контракті.
Щоб шаблон проксі працював, потрібно написати користувацьку резервну функцію, яка визначає, як проксі-контракт має обробляти виклики функцій, які він не підтримує. У цьому випадку резервна функція проксі-сервера запрограмована на запуск delegatecall і перенаправлення запиту користувача на поточну реалізацію логічного контракту.
Проксі-контракт є незмінним за замовчуванням, але можна створювати нові логічні контракти з оновленою бізнес-логікою. Виконання оновлення — це лише питання зміни адреси логічного контракту, на який посилається проксі-контракт.
Направивши проксі-контракт на новий логічний контракт, змінюється код, який виконується, коли користувачі викликають функцію проксі-контракту. Це дозволяє нам оновлювати логіку контракту, не вимагаючи від користувачів взаємодії з новим контрактом.
Шаблони проксі є популярним методом оновлення смарт-контрактів, оскільки вони усувають труднощі, пов'язані з міграцією контрактів. Однак шаблони проксі складніші у використанні і можуть призвести до критичних недоліків, таких як зіткнення селекторів функцій (opens in a new tab), якщо їх використовувати неправильно.
Докладніше про шаблони проксі (opens in a new tab).
Механізм оновлення № 4: шаблон стратегії
Цей метод створений під впливом шаблону стратегії (opens in a new tab), який заохочує створення програм, що взаємодіють з іншими програмами для реалізації певних функцій. Застосування шаблону стратегії до розробки Ethereum означатиме створення смарт-контракту, який викликає функції з інших контрактів.
Основний контракт у цьому випадку містить основну бізнес-логіку, але взаємодіє з іншими смарт-контрактами («супутниковими контрактами») для виконання певних функцій. Цей основний контракт також зберігає адресу кожного супутникового контракту і може перемикатися між різними реалізаціями супутникового контракту.
Ви можете створити новий супутниковий контракт і налаштувати основний контракт із новою адресою. Це дозволяє змінювати стратегії (тобто реалізовувати нову логіку) для смарт-контракту.
Хоча шаблон стратегії схожий на обговорений раніше шаблон проксі, він відрізняється тим, що основний контракт, з яким взаємодіють користувачі, містить бізнес-логіку. Використання цього шаблону дає вам можливість вносити обмежені зміни до смарт-контракту, не впливаючи на основну інфраструктуру.
Основний недолік полягає в тому, що цей шаблон здебільшого корисний для впровадження незначних оновлень. Крім того, якщо основний контракт зламано (наприклад, через злом), ви не зможете використовувати цей метод оновлення.
Механізм оновлення № 5: алмазний шаблон
Алмазний шаблон можна вважати вдосконаленням шаблону проксі. Алмазні шаблони відрізняються від шаблонів проксі тим, що проксі-контракт алмазного шаблону може делегувати виклики функцій більш ніж одному логічному контракту.
Логічні контракти в алмазному шаблоні відомі як фасети. Щоб алмазний шаблон працював, потрібно створити зіставлення в проксі-контракті, яке зіставляє селектори функцій (opens in a new tab) з різними адресами фасетів.
Коли користувач робить виклик функції, проксі-контракт перевіряє зіставлення, щоб знайти фасет, відповідальний за виконання цієї функції. Потім він викликає delegatecall (використовуючи резервну функцію) і перенаправляє виклик до відповідного логічного контракту.
Алмазний шаблон оновлення має деякі переваги над традиційними шаблонами оновлення проксі:
-
Він дозволяє оновити невелику частину контракту, не змінюючи весь код. Використання шаблону проксі для оновлень вимагає створення абсолютно нового логічного контракту, навіть для незначних оновлень.
-
Усі смарт-контракти (включно з логічними контрактами, що використовуються в шаблонах проксі) мають обмеження розміру 24 КБ, що може бути обмеженням, особливо для складних контрактів, які вимагають більше функцій. Алмазний шаблон дозволяє легко вирішити цю проблему, розділивши функції між кількома логічними контрактами.
-
Шаблони проксі використовують універсальний підхід до контролю доступу. Суб'єкт, що має доступ до функцій оновлення, може змінити весь контракт. Але алмазний шаблон дає змогу застосувати модульний підхід до дозволів, завдяки якому ви можете обмежити для суб’єктів можливість оновлювати лише певні функції у смарт-контракті.
Докладніше про алмазний шаблон (opens in a new tab).
Плюси та мінуси оновлення смарт-контрактів
| Переваги | Недоліки |
|---|---|
| Оновлення смарт-контракту може полегшити виправлення вразливостей, виявлених на етапі після розгортання. | Оновлення смарт-контрактів заперечує ідею незмінності коду, що має наслідки для децентралізації та безпеки. |
| Розробники можуть використовувати оновлення логіки для додавання нових функцій до децентралізованих застосунків. | Користувачі повинні довіряти розробникам, що ті не будуть довільно змінювати смарт-контракти. |
| Оновлення смарт-контрактів можуть підвищити безпеку для кінцевих користувачів, оскільки помилки можна швидко виправити. | Програмування функціональності оновлення в смарт-контрактах додає ще один рівень складності і збільшує ймовірність критичних недоліків. |
| Оновлення контрактів дають розробникам більше простору для експериментів з різними функціями і вдосконалення децентралізованих застосунків з часом. | Можливість оновлення смарт-контрактів може спонукати розробників швидше запускати проєкти, не проводячи належної перевірки на етапі розробки. |
| Ненадійний контроль доступу або централізація в смарт-контрактах може полегшити зловмисникам виконання несанкціонованих оновлень. |
Що слід враховувати під час оновлення смарт-контрактів
-
Використовуйте безпечні механізми контролю доступу/авторизації для запобігання несанкціонованим оновленням смарт-контрактів, особливо якщо використовуються шаблони проксі, шаблони стратегій або розділення даних. Прикладом є обмеження доступу до функції оновлення, щоб її міг викликати лише власник контракту.
-
Оновлення смарт-контрактів — це складна діяльність, яка вимагає високого рівня ретельності, щоб запобігти появі вразливостей.
-
Зменште припущення щодо довіри, децентралізувавши процес впровадження оновлень. Можливі стратегії включають використання контракту гаманця з мультипідписом для контролю оновлень або вимогу до членів DAO голосувати за затвердження оновлення.
-
Пам'ятайте про витрати, пов'язані з оновленням контрактів. Наприклад, копіювання стану (наприклад, балансів користувачів) зі старого контракту в новий під час міграції контракту може вимагати більше однієї транзакції, що означає більші комісії за газ.
-
Розгляньте можливість впровадження блокування за часом для захисту користувачів. Блокування за часом — це затримка, яка застосовується до змін у системі. Блокування за часом можна поєднувати з системою керування з мультипідписом для контролю оновлень: якщо запропонована дія досягає необхідного порогу схвалення, вона не виконується, доки не мине попередньо визначений період затримки.
Блокування за часом дає користувачам певний час, щоб вийти із системи, якщо вони не згодні із запропонованою зміною (наприклад, оновленням логіки або новими схемами комісій). Без блокування за часом користувачі повинні довіряти розробникам, що вони не будуть вносити довільні зміни до смарт-контракту без попереднього повідомлення. Недоліком тут є те, що блокування за часом обмежує можливість швидкого виправлення вразливостей.
Ресурси
Плагіни оновлення OpenZeppelin — набір інструментів для розгортання та захисту смарт-контрактів, що оновлюються.
Навчальні посібники
- Оновлення ваших смарт-контрактів | посібник на YouTube (opens in a new tab) від Патріка Коллінза
- Посібник з міграції смарт-контрактів Ethereum (opens in a new tab) від Остіна Гріффіта
- Використання шаблону проксі UUPS для оновлення смарт-контрактів (opens in a new tab) від Пранеша А.С.
- Посібник із Web3: написання смарт-контракту, що оновлюється (проксі), за допомогою OpenZeppelin (opens in a new tab) від fangjun.eth
Для подальшого читання
- Стан оновлень смарт-контрактів (opens in a new tab) від Сантьяго Палладіно
- Кілька способів оновлення смарт-контракту Solidity (opens in a new tab) — блог Crypto Market Pool
- Дізнайтеся: оновлення смарт-контрактів (opens in a new tab) — документація OpenZeppelin
- Шаблони проксі для можливості оновлення контрактів Solidity: прозорі проксі проти UUPS-проксі (opens in a new tab) від Навіна Саху
- Як працюють алмазні оновлення (opens in a new tab) від Ніка Маджа