Ты прочёл контракт. Каждую строку. Код был чист — ни скрытого mint, ни осушения владельцем, ни honeypot-комиссии. Ты отправил средства с чистой совестью. А неделей позже они исчезли, забранные кодом, которого не было, когда ты смотрел.
Добро пожаловать в обновляемый контракт: важнейшую вещь, которую надо понять о риске смарт-контрактов, и ту, которую большинство даже не проверяет. Потому что вот трюк, что ломает всё обещание «не доверяй, проверяй»: если контракт обновляемый, то проверка кода сегодня не говорит тебе почти ничего о коде, что реально будет держать твои деньги завтра.
Неизменяемость блокчейна должна быть его сутью. Код — закон, закон не меняется, на него можно опереться. Обновляемость тихо стирает эту гарантию — и делает это во имя очень разумно звучащего слова: удобство.
Зачем обновляемые контракты вообще существуют
Начни с честного случая, потому что он есть. У неизменяемого кода жестокая обратная сторона: если есть баг, его не починить. Опечатка в смарт-контракте, держащем миллиард, — это миллиардная опечатка, вечная, незалатываемая. Целые протоколы гибли от единственной непочинимой строки.
И разработчики изобрели паттерн proxy. Вместо одного контракта, держащего и логику, и средства, ты их разделяешь. Контракт-proxy держит средства и состояние и перенаправляет каждый вызов отдельному контракту-implementation, где живёт логика, — через механизм под названием `delegatecall`. Когда хочешь сменить логику, ты не трогаешь средства. Ты просто наводишь proxy на новый implementation. Тот же адрес, те же балансы, совершенно новое поведение.
Это решает реальную задачу. Ты можешь залатать критический баг, не прося миллион пользователей мигрировать. Можешь добавить функции. В молодом, быстро движущемся пространстве, полном эксплуатируемых ошибок, обновляемость часто — ответственный выбор. Никто об этом не лжёт.
Точный миг, когда удобство становится backdoor
Теперь смотри, что ты только что вручил. Власть «навести proxy на новый implementation» — это власть заменить весь код, в любой момент, на что угодно. Код, что ты проаудировал, может быть заменён на код, которого ты никогда не видел, — после того, как твои деньги заперты внутри.
Это не баг паттерна. Это и есть паттерн. Обновляемый контракт по определению — контракт, чьи правила может переписать тот, кто держит ключ обновления. Твой аудит заверил честную версию. Он не сказал ничего о версии, что держатель ключа развернёт в следующий вторник.
И реальный вопрос — единственный вопрос — перестаёт быть «безопасен ли код?» и становится: кто держит ключ обновления и что мешает ему злоупотребить? Безопасность обновляемого контракта не в его коде. Она в хранении этого ключа. Ты больше не доверяешь математике. Ты доверяешь человеку или группе — ровно как старому банку; ты просто переместил доверие и сказал себе, что сбежал от него.
Спектр хранения: от backdoor до ограждения
Не все обновляемые контракты равны. Обновляемость кода нейтральна. Управление обновлением решает всё. Вот спектр, от худшего к лучшему.
Единственный внешний аккаунт (EOA). Кошелёк одного человека может сменить код. Это голый backdoor с дружелюбным именем. Одна фишинговая сид-фраза — его — и средства каждого пользователя обнажены. Даже если держатель — святой, он теперь единая точка отказа и единая точка принуждения. Относись так: доверь этому одному человеку всё, навсегда.
Мультисиг. Несколько подписантов должны согласиться на обновление. Лучше — нет одинокого ключа, труднее зафишить, труднее принудить. Но если подписанты сговорятся или скомпрометируют достаточно ключей, backdoor открывается вновь. Спроси: сколько подписантов, независимы ли они, публичны ли?
Мультисиг за timelock. Теперь обновление должно быть публично поставлено в очередь и выждать задержку прежде исполнения. Вот настоящий скачок в безопасности. Ты можешь видеть новый код на подходе прежде, чем он приземлится, осмотреть его и выйти, если он злонамерен. Backdoor всё ещё есть — но теперь он открывается замедленно, на публике, с аварийным люком.
Голосование DAO плюс timelock или отречённая обновляемость. Толпа управляет изменением, в открытую, с задержкой, — или команда отрекается (renounce) от власти обновления целиком, делая контракт истинно неизменяемым. Отречение выменивает способность чинить баги на гарантию, что никто и никогда не перепишет правила. Для зрелого, проаудированного контракта этот обмен часто ровно верен.
Наша запись
Вот глубинная форма. Неизменяемый код — это Хека, творящее слово, сказанное однажды и сделанное постоянным: неизменное, а потому достойное доверия, одинаковое для всех навсегда. Это постоянство — и есть вся причина, по которой слову можно верить. Оно не может льстить тебе сегодня и предать завтра, потому что не может измениться.
Ключ обновления — рука, что может разговорить слово и сказать новое. В верных руках — ограниченных, под timelock, наблюдаемых толпой — это рука целителя, приберегаемая для врачевания подлинной раны в коде. В одной скрытой руке это любимый инструмент Исфет: обещание, что сдвигается после того, как ты на него положился, контракт, что значит одно, когда ты подписываешь, и другое, когда он действует. Всё искусство паразита — правило, что тихо меняется после того, как ты выстроил на нём жизнь. Ненаблюдаемый ключ обновления — это искусство, отлитое в код. Вопрос никогда не в том, может ли слово измениться. Он в том, происходит ли изменение на свету, на Весах, где каждый может взвесить новое слово прежде, чем оно станет законом.
Рычаг
У тебя теперь есть проверка, которую большинство крипто-пользователей никогда не делали, и она занимает две минуты.
Для любого контракта, что вот-вот будет держать твои деньги, выясни: Обновляем ли он? Поищи в коде и документации `proxy`, `implementation`, `upgradeable`. Если да — и это часть, которую почти никто не делает, — выясни, кто контролирует обновление.
Затем суди по хранению, а не по коду:
- Единственный EOA контролирует обновления → относись как к backdoor. Считай, что код может стать злонамеренным. Рискуй лишь тем, что вручил бы незнакомцу.
- Мультисиг → лучше; проверь, сколько подписантов, независимы ли и публичны ли они.
- Timelock-мультисиг или DAO → честный паттерн; ты видишь обновления на подходе и можешь выйти.
- Отречённый / неизменяемый → код, что ты прочёл, — это код, что ты получишь. Высшая уверенность, без правки багов.
Не бойся обновляемости рефлекторно — бойся неподотчётной обновляемости. Путь обновления под timelock, управляемый публично, — это ограждение. Одинокий скрытый ключ — заряженное ружьё с твоим именем на магазине.
Неизменяемое — это обещание. Обновляемое — обещание, что кто-то может переписать. Прежде чем довериться любому, найди руку, что держит перо, — и убедись, что она не может двигаться во тьме.