
Вступ
Смарт-контракти запрограмовані таким чином, що після їх розгортання в мережі блокчейну вони стають постійними і незмінними. Ця незмінність є джерелом безпеки і довіри, але також створює проблеми, коли розробникам потрібно виправити помилки або додати нові функції.
Коли потрібні зміни, розробники мають випустити повністю новий контракт з іншою адресою. Хоча така стабільність є чудовою для безпеки, вона може бути обмежувальною в ситуаціях, коли щось йде не так або потрібно щось поліпшити.
Історія показує, що вразливість розумних контрактів може призвести до катастрофічних фінансових втрат. Одним із конкретних прикладів є серйозний інцидент, в результаті якого через вразливість, якою можна було скористатися, було втрачено мільйони доларів.
Ці інциденти вказують на важливість можливості вирішувати проблеми безпеки та виправляти помилки після розгортання. Можливість оновлення контрактів дозволяє створити механізм для виправлення проблем без втрати існуючого стану та користувацького досвіду, що є ключовим аспектом можливості оновлення смарт-контрактів.
Ця здатність може бути важливою для запобігання значних втрат і збереження цілісності системи.
Існує безліч способів реалізації оновлюваних смарт-контрактів, таких як проксі-шаблони смарт-контрактів та методи розділення даних. Ця стаття присвячена саме тому, як реалізувати оновлюваність за допомогою проксі-шаблонів, що є популярним способом вирішення цієї проблеми в сфері розробки блокчейнів.
Розуміння шаблону проксі
Проксі-шаблон — це структурний шаблон проектування, в якому один контракт реалізує інтерфейс для іншого контракту. Ця архітектура складається з двох основних частин: проксі-контракту та контракту реалізації.
Замість того, щоб користувач безпосередньо спілкувався з контрактом реалізації, він буде спілкуватися з проксі-контрактом, який потім передаватиме запити відповідно.
У цій конфігурації як проксі-контракт, так і контракт реалізації не змінюються після розгортання. Однак можливість оновлення досягається за рахунок того, що проксі може з часом посилатися на різні контракти реалізації.
Це означає, що користувачі можуть продовжувати використовувати ту саму адресу та інтерфейс, поки оновлюється функціональність основи. З точки зору користувача, програма продовжує працювати бездоганно, навіть якщо логіка бекенду змінюється.
Проксі-контракт відповідає за взаємодію з користувачами та зберігання всіх даних. Він зберігає адресу контракту реалізації в спеціальному місці зберігання.
Коли користувачі викликають функції в контракті-проксі, викликається резервна функція. Наступна функція використовує механізм delegatecall ethereum для виклику коду з контракту реалізації, але будь-які зміни стану зберігаються в сховищі контракту-проксі.
Основні реалізації проксі-шаблонів
Прозорий проксі-шаблон
Шаблон прозорого проксі розміщує функціональність оновлення в самому контракті проксі. Проксі має метод upgradeTo, який використовується для оновлення адреси, що вказує на контракт реалізації.
Це створює потенційну проблему: якщо і проксі-контракт, і контракт реалізації містять метод upgradeTo, не зрозуміло, який з них слід викликати, коли користувач запускає цю функцію.
Щоб вирішити цю неоднозначність, було розроблено рішення, за яким рішення про те, кому делегувати повноваження, базується на тому, хто викликає контракт.
Якщо виклик здійснюється адміністратором проксі, виклики обробляються самим контрактом проксі. В іншому випадку виклики делегуються контракту реалізації. Такий підхід забезпечує чітке виконання функцій і уникнення плутанини між адміністративними та звичайними операціями користувачів.
Універсальний стандарт проксі-серверів, що підлягають оновленню
Універсальний стандарт оновлюваного проксі має інший підхід, розміщуючи функцію оновлення в контракті реалізації, а не в проксі. Проксі делегує всі виклики контракту реалізації, який має метод upgradeTo для вказівки на новіші версії.
Цей шаблон дає розробникам більше контролю над процесом оновлення. Якщо розробник вирішить більше не включати метод upgradeTo в майбутню версію, контракт тепер є незмінним і не може бути оновлений.
Це може бути корисно, коли контракт був добре протестований і команда розробників хоче зафіксувати його в остаточному вигляді.
Оскільки логіка оновлення міститься в контракті реалізації, проксі-серверу не потрібно перевіряти, чи є виклик адміністратором, перш ніж делегувати виклики.
Це робить шаблон більш ефективним з точки зору використання ресурсів, ніж шаблон прозорого проксі. Також усувається потенційний конфлікт імен, оскільки метод upgradeTo існує тільки в контракті реалізації.
Шаблон проксі-маяка
Шаблон Beacon Proxy Pattern представляє архітектуру, що складається з трьох компонентів: контракту проксі, контракту маяка та контракту реалізації. Замість збереження адреси реалізації, проксі зберігає адресу контракту маяка.
Контракт маяка, у свою чергу, містить адресу контракту реалізації.
Коли користувач викликає проксі, спочатку він отримує адресу контракту маяка, а потім викликає маяк, щоб отримати адресу контракту реалізації. Нарешті, він залишає виклик контракту реалізації.
Цей додатковий рівень непрямої вказівки має дуже конкретну мету.
Цей шаблон особливо корисний, коли кілька проксі-контрактів повинні використовувати одну і ту ж реалізацію. У простіших шаблонах для оновлення реалізації потрібно було б оновлювати адресу в кожному проксі-контракті окремо. З шаблоном маяка потрібно оновлювати тільки контракт маяка, і всі пов'язані з ним проксі автоматично вказують на нову реалізацію.
Основні реалізації проксі-шаблонів
Шаблон «діамантового проксі»
Шаблон Diamond Proxy вирішує проблему обмеження розміру смарт-контрактів, які зазвичай обмежуються приблизно 24 кілобайтами. Цей шаблон розділяє функціональність на кілька менших контрактів, відомих як фасети.
Проксі-контракт зберігає відповідність між селекторами функцій та адресою фасетів, що містять ці функції.
Коли функція викликається на проксі, вона використовує селектор функції для пошуку фасету, що містить функцію, і делегує виклик функції відповідному фасету.
Оновлення здійснюються шляхом модифікації адрес фасетів у проксі. Ця схема забезпечує набагато більшу загальну функціональність завдяки розподілу функціональності між декількома контрактами, при цьому кожен фасет залишається нижче обмеження розміру.
Порівняння підходів до проксі-шаблонів
Кожен шаблон проксі має різні характеристики, що роблять його придатним для різних випадків використання. Усі чотири шаблони потребують контракту проксі та використання делегування для переадресації викликів до контрактів реалізації.
Прозорий проксі-шаблон розміщує функції оновлення в проксі-контракті, а UUPS розміщує функції оновлення в контракті реалізації. Шаблон Beacon розміщує функцію оновлення у власному контракті Beacon, а шаблон Diamond зазвичай розміщує функцію оновлення в контрактах реалізації, але це не є строго визначеним.
Що стосується незмінності, то шаблони UUPS і Diamond можуть зробити контракти остаточно незмінними, видаливши функцію оновлення з майбутніх версій. Шаблони Transparent і Beacon не забезпечують такої гнучкості.
При роботі з декількома проксі-серверами модель Beacon має безперечну перевагу. У моделях Transparent, UUPS і Diamond кожен проксі-сервер потрібно змінювати окремо при впровадженні нової реалізації.
З використанням шаблону Beacon необхідно оновлювати лише контракт Beacon, і всі проксі автоматично використовуватимуть нову реалізацію.
Ефективність використання ресурсів відрізняється для кожного шаблону. Шаблон Transparent вимагає перевірки того, чи є виклик адміністратором, перед кожним делегуванням, тому він є більш витратним.
UUPS є більш ефективним, оскільки не вимагає цієї перевірки. Усі шаблони, крім Diamond, мають додаткові витрати газу на додаткові операції пошуку.
Усі шаблони, крім Diamond, обмежені 24 кілобайтами на контракт. Шаблон Diamond дозволяє кожному аспекту бути розміром до 24 кілобайт, що дає можливість мати набагато більшу загальну функціональність у декількох аспектах.
Опануйте безпеку смарт-контрактів
Навчіться просунутим технікам створення безпечних контрактів, що можна оновлювати, завдяки нашим курсам під керівництвом експертів.
Важливі міркування та ризики
Проблеми зі зберіганням даних
При реалізації проксі-шаблонів існує серйозний ризик колізій у сховищі. Змінні контракту зберігаються в конкретних слотах сховища, і якщо змінні проксі-контракту та змінні контракту реалізації зберігаються в одних і тих самих слотах сховища, вони будуть взаємодіяти між собою.
Крім того, якщо порядок змінних у контракті реалізації змінюється від однієї версії до іншої, слоти пам'яті можуть бути перепризначені, що призведе до пошкодження даних.
Неініціалізовані контракти на реалізацію
Контракти реалізації повинні бути ініціалізовані точно один раз за допомогою функції ініціалізації, яка є аналогічною концепцією конструктора в традиційних контрактах.
Розробники також забувають ініціалізувати реалізацію або не включають захист, щоб запобігти повторному виклику функції ініціалізації.
У такому випадку зловмисники можуть самостійно викликати функцію ініціалізації та потенційно взяти під контроль контракт або маніпулювати його станом.
Реальні інциденти безпеки
Програма винагороди за виявлення помилок виявила критичну вразливість у контрактах проксі-серверів сховища, де контракти реалізації не були ініціалізовані правильно. Ця вразливість могла бути використана зловмисником для знищення контракту реалізації, що зробило б пов'язані контракти проксі-серверів марними. В іншому інциденті, що стався в липні, смарт-контракти були зламані через вразливість в коді ініціалізації, в якому функція ініціалізації могла бути викликана кілька разів.
Альтернативні підходи до модернізації
Шаблон розділення даних
Альтернативою проксі-шаблонам є підхід до розділення даних. Цей підхід базується на використанні окремих контрактів для зберігання та логіки. Логічний контракт взаємодіє з контрактом зберігання для читання або оновлення даних.
Хоча логічний контракт можна замінити новими версіями, контракт зберігання є фіксованим і незмінним. Це пропонує іншу модель оновлення, яка може бути доречною в деяких випадках.
Рівні перевірки
Жоден із загальновживаних проксі-шаблонів не має механізмів перевірки дійсності нового контракту впровадження перед оновленням.
Нові версії обов'язково повинні містити всю необхідну бізнес-логіку, резервні функції та інші важливі компоненти.
Це можна досягти шляхом введення рівня перевірки, який знаходиться поряд з рівнем проксі, рівнем бізнес-логіки та рівнем зберігання. Цей додатковий рівень гарантує, що оновлення відповідають певним критеріям, перш ніж їм буде дозволено виконуватися.
Кращі практики для контрактів, що підлягають оновленню
Розробники, які проектують контракти з можливістю оновлення, повинні дотримуватися деяких ключових рекомендацій для забезпечення безпеки та надійності:
- •Натомість покладайтеся на перевірені реалізації з перевірених бібліотек, які були ретельно перевірені та проаналізовані
- •Завжди переконайтеся, що контракти реалізації ініціалізовані належним чином і що функції ініціалізації можна викликати тільки один раз
- •Ніколи не ініціалізуйте змінні стану при їх оголошенні або в конструкторі, використовуйте функцію ініціалізації для налаштування всіх станів
- •Не змінюйте порядок або типи змінних стану при створенні нових версій контрактів на реалізацію
- •Якщо потрібні нові змінні, їх слід додавати після всіх існуючих змінних
- •Для реалізації алгоритму Diamond використовуйте спеціальні методи зберігання, оптимізовані для цього алгоритму
- •Метод UUPS є кращим за прозорий проксі-патерн, якщо це можливо, оскільки він вимагає менше газу для рутинних операцій
- •Переконайтеся, що обліковий запис адміністратора проксі-сервера є високозахищеним, оскільки цей обліковий запис контролює процес оновлення і є критично важливим елементом безпеки
- •Нарешті, перед впровадженням всі контракти повинні бути професійно перевірені досвідченими фахівцями з безпеки смарт-контрактів.
Останні думки
Проксі-шаблони забезпечують потужний механізм для оновлення смарт-контрактів, зберігаючи при цьому ту саму адресу та стан. Проксі-контракт використовує delegateCall для передачі виконання контрактам реалізації, щоб можна було змінювати базову логіку без модифікації користувацького інтерфейсу.
Однак, якщо оновлювані контракти не реалізовані належним чином, вони можуть створити серйозні вразливості в системі безпеки.
Розробники повинні ретельно зважити всі «за» і «проти» різних типів проксі-шаблонів, дотримуватися встановлених найкращих практик і забезпечити проведення належних аудитів безпеки для створення надійних і безпечних смарт-контрактів, які можна оновлювати.
Порівняння проксі-шаблонів
При порівнянні проксі-шаблонів, які слід використовувати, необхідно враховувати кілька факторів:
Порівняння проксі-шаблонів
| Особливість | Прозорий | UUPS | Beacon | Діамант |
|---|---|---|---|---|
| Місце оновлення | Проксі-контракт | Договір про впровадження | Контракт Beacon | Контракти на впровадження |
| Постійна незмінність | Ні | Так | Ні | Так |
| Кілька проксі-серверів | Індивідуальні оновлення | Індивідуальні оновлення | Оновлення одного маяка | Індивідуальні оновлення |
| Ефективність використання газу | Вища вартість (адміністративна перевірка) | Нижча вартість | Середній (додатковий пошук) | Середній (пошук за фасетами) |
| Обмеження розміру контракту | 24 КБ | 24 КБ | 24 КБ | 24 КБ на фасет |
| Складність реалізації | Середній | Середній | Середній | Середній |
Розгляди щодо вибору шаблону
Ключові міркування щодо вибору шаблону:
- •Усі шаблони потребують проксі-контракту та використовують делегування для передачі викликів
- •Прозорий проксі-шаблон використовується для зберігання функцій оновлення в самому проксі-контракті
- •UUPS розміщує функції оновлення в контракті реалізації
- •Шаблон Beacon використовує окремий контракт Beacon для оновлень
- •У моделі «Діамант» функції оновлення зазвичай розміщуються в контрактах реалізації, але специфікація не вимагає цього.
- •Тільки UUPS і Diamond мають можливість зробити контракти постійно незмінними, відмовившись від функції оновлення в майбутніх версіях
- •Шаблон Beacon ідеально підходить, якщо вам потрібно одночасно оновити кілька проксі-серверів, оскільки оновлювати потрібно тільки маяк, а не кожен проксі-сервер окремо.
- •Максимальний розмір контракту становить 24 кілобайти для шаблонів Transparent, UUPS та Beacon
- •Шаблон Diamond підтримує розмір кожного аспекту до 24 кілобайт, тому можна підтримувати набагато більшу загальну функціональність
- •Усі шаблони мають середній рівень складності реалізації, а для шаблонів Transparent, UUPS та Beacon існують добре налагоджені бібліотеки


