Кто и как может изменить смарт-контракт после запуска

Считается, что смарт-контракт - это абсолютно не изменяемая форма договора. Однако на самом деле логику можно сделать другой, что позволяет исправлять ошибки в протоколах
Кто и как может изменить смарт-контракт после запуска
Источник: РБК Крипто

Смарт-контракт принято считать неизменяемым, но многие приложения устроены так, что после запуска их логику можно заменить. Для этого применяют прокси-контракты: адрес сервиса и данные пользователей остаются прежними, а программу, которая обрабатывает операции, обновляют отдельно. Такая схема позволяет исправлять ошибки без переноса активов, но оставляет управляющей стороне возможность менять правила уже работающего протокола.

Как работает прокси

Пользователь отправляет транзакцию на адрес прокси. Тот с помощью делегирующего вызова выполняет код другого контракта — реализации, но использует баланс и хранилище самого прокси. Поэтому после обновления не меняются адрес приложения, токены, позиции и настройки. Меняется программа, которая читает и обрабатывает эти данные.

Во многих прокси адрес реализации записывают в специальный слот по стандарту ERC-1967. Отдельные слоты предусмотрены для администратора и контракта-маяка. Это помогает обозревателям блокчейна распознавать прокси и показывать связанную с ним реализацию.

Само наличие прокси еще не означает, что контракт можно обновить. Некоторые конструкции только перенаправляют вызовы на заранее заданную реализацию и не позволяют ее заменить. Поэтому проверять нужно не название архитектуры, а конкретные полномочия управляющей стороны.

Кто принимает решение

Новую реализацию может назначать один кошелек, мультиподпись, DAO или отдельный контракт управления. Мультиподпись снижает зависимость от одного ключа, но не отменяет доверия к подписантам. Например, в схеме «2 из 3» двух скомпрометированных ключей достаточно для проведения обновления.

Дополнительную защиту дает таймлок — задержка между одобрением новой версии и ее запуском. Она оставляет время на проверку кода и вывод средств. Но задержка бесполезна, если новую реализацию не публикуют заранее или администратор способен остановить вывод активов.

Захват управления может оказаться опаснее ошибки в действующей версии. Злоумышленник может установить код, который выводит средства, блокирует операции, меняет комиссии или назначает новые административные роли. Поэтому проверка одной реализации не гарантирует, что следующая версия сохранит те же свойства.

Где возникают ошибки

Конструктор реализации выполняется при ее собственном развертывании и не настраивает хранилище прокси. Начальные параметры поэтому задают отдельной функцией, обычно инициализации. Ее необходимо защитить от повторного вызова: иначе посторонний может назначить себя владельцем или получить другую привилегированную роль.

Саму реализацию также оставляют недоступной для инициализации. OpenZeppelin рекомендует блокировать эту возможность через вызов функции _disableInitializers, чтобы атакующий не смог захватить неинициализированный контракт реализации.

Другая группа рисков связана со структурой хранилища. Новая версия должна читать прежние данные из тех же ячеек. Перестановка переменных или изменение их типов может исказить балансы, настройки и права доступа. Поэтому перед обновлением проверяют совместимость старой и новой структуры хранения.

Какие схемы используют

В transparent proxy механизм обновления находится в самом прокси. Вызовы администратора обрабатываются отдельно и не передаются пользовательской реализации. Это разделяет служебные и обычные операции.

В UUPS-прокси механизм обновления находится в реализации. Сам прокси получается компактнее, но каждая новая версия должна сохранять корректную проверку полномочий и совместимость с этой схемой. Ошибка может либо открыть обновление посторонним, либо заблокировать установку следующих версий.

Beacon proxy применяют для группы контрактов. Каждый прокси получает адрес реализации из общего beacon-контракта. Изменение beacon обновляет сразу всю группу: это упрощает управление, но увеличивает масштаб возможной ошибки.

Как проверить контракт

Сначала нужно открыть его адрес в обозревателе блокчейна, определить, является ли он прокси, и перейти к текущей реализации. Метка «Verified» подтверждает соответствие опубликованного исходного кода байткоду только по выбранному адресу. Она не означает, что этот код нельзя заменить.

Затем стоит проверить владельца административных прав, порог мультиподписи, наличие механизма задержки выполнения административных действий в смарт-контрактах и доступные управляющей стороне функции. События Upgraded (обновление контракта), события AdminChanged (смена администратора) и BeaconUpgraded (обновление beacon-прокси) помогают восстановить историю обновлений и смены контроля.

Обновляемость сама по себе не делает протокол ненадежным. Она позволяет исправлять критические ошибки без миграции пользователей. Но для оценки такого контракта недостаточно изучить действующий код. Нужно также знать, кто вправе его заменить, сколько времени занимает обновление и может ли пользователь вывести средства до вступления изменений в силу.