EIP-4788: учим EVM доверять beacon-цепочке
После The Merge консенсус-слой хранит ровно те данные, которые нужны контрактам стейкинга и рестейкинга, — балансы валидаторов, статусы, слэшинги, — но EVM не видит из этого ничего, и проекты опирались на доверенные оракулы. EIP-4788 раскрывает корень родительского beacon-блока внутри EVM, так что контракт может проверить любой факт консенсуса доказательством Меркла — и без оракула.
- The Merge (два слоя)
- доказательства Меркла
После The Merge самые интересные данные Ethereum — кто валидатор, его баланс, был ли он слэшнут, его credentials для вывода — живут на консенсус-слое, а слой исполнения (EVM, где выполняется каждый смарт-контракт) не видит из этого ни байта. При этом именно на этих данных построены ликвидный стейкинг (Lido), рестейкинг (EigenLayer) и протоколы валидаторских доказательств. Не имея нативного доступа, они откатывались к доверенным оракулам: комитет публиковал факты консенсуса ончейн, и вам приходилось просто доверять. EIP-4788 убирает комитет. Давайте выведем, как.
Проблема: состояние консенсуса прямо здесь — и недостижимо
Узел после The Merge исполняет два слоя, но смарт-контракт выполняется целиком внутри мирового состояния слоя исполнения. состояние beacon-цепочки (beacon state) Состояние консенсус-слоя: полный реестр валидаторов — баланс, статус активации/выхода, флаг слэшинга, credentials для вывода каждого валидатора, — плюс RANDAO и прочее. Именно на этом работает proof of stake, и это невидимо для EVM. находится в другом слое, и нет ни опкода, ни прекомпайла, ни поля, чтобы до него дотянуться. Так что контракт стейкинга, желающий узнать, «держит ли валидатор N всё ещё 32 ETH, и не слэшнут ли он», не имеет доверенного способа это выяснить. Единственным вариантом был оракул (oracle) Офчейн-комитет (часто мультиподпись), который читает состояние консенсуса и публикует его в контракт слоя исполнения. Пользователям приходится доверять, что он честен и жив, — центральная точка отказа, надстроенная над децентрализованной цепочкой. : какая-то доверенная сторона следит за beacon-цепочкой и записывает ответ в контракт.
Именно такое предположение о доверии меньше всего хочется иметь под миллиардами застейканных ETH. Если оракул лжёт или уходит в офлайн, построенный на нём протокол ломается.
→ Шаг 2: раскрываем коммитмент, а не данные.
Вам не нужно состояние — вам нужен его корень
Beacon-цепочка уже хеширует всё своё состояние в единый 32-байтовый коммитмент: корень beacon-блока (beacon block root) Hash-tree-root beacon-блока, который рекурсивно коммитит (через SSZ-меркилизацию) ко всему состоянию beacon-цепочки. Любой отдельный факт — баланс конкретного валидатора — это лист под этим корнем, доказуемый веткой Меркла. . Поскольку этот корень — корень Меркла по всему состоянию beacon-цепочки, любой отдельный факт — баланс одного валидатора, его статус слэшинга — это лист, который можно доказать веткой Меркла. Так что контракту, держащему подлинный недавний beacon-корень, не нужно, чтобы состояние копировали внутрь: кто угодно может вручить ему доказательство Меркла, а контракт проверит доказательство против корня, которому доверяет. Дорогое глобальное состояние схлопывается до одного хеша плюс короткое доказательство на запрос.
Это переформулирует всю задачу. Протоколу не нужно отдавать состояние beacon-цепочки в EVM — ему нужно лишь сделать доступной и заслуживающей доверия одну вещь: недавний, подлинный корень beacon-блока.
→ Шаг 3: пусть сам протокол записывает корень.
Протокол штампует корень родительского beacon-блока каждый блок
Корень должен помещать протокол, а не пользователь. Поэтому EIP-4788 добавляет поле parentBeaconBlockRoot в заголовок блока исполнения (каждый блок исполнения уже соответствует beacon-блоку, так что знает корень своего родителя), и в самом начале обработки каждого блока системный вызов — от специального системного адреса 0xfff…ffe, который никто не может подделать, — записывает пару (timestamp → parentBeaconBlockRoot) в предразвёрнутый контракт beacon-корней (beacon roots contract) Предразвёрнутый системный контракт по адресу 0x000F3df6…beac02, хранящий кольцевой буфер недавних пар (timestamp → beacon-корень) (~8191 слотов, около 27 часов). Протокол пишет в него каждый блок; контракты читают его, вызывая с timestamp. . Этот контракт — кольцевой буфер из ~8191 недавних слотов (около 27 часов), так что самые свежие корни всегда доступны для запроса, а старые вытесняются.
- 1 временная метка блока — ключ, по которому вы запрашиваете предеплой
- 2 размер кольцевого буфера: ~27 часов корней (простое число, чтобы равномерно распределять записи)
→ Шаг 4: читаем корень, доказываем факт.
Выигрыш: подлинный корень, затем доказательство
Теперь любой контракт может сделать полностью ончейн то, для чего раньше требовался оракул. Он вызывает предеплой beacon-корней с timestamp и получает обратно подлинный beacon-корень для этого слота. Затем он принимает доказательство Меркла — предоставленное тем, кто хочет что-то доказать, — что баланс (или статус, или credential для вывода) конкретного валидатора является листом под этим корнем, и проверяет доказательство. Если оно сходится, факт доказан; если нет — отклонён. Никакого комитета, никакого доверия сверх самого протокола.