Должен ли Ethereum включать еще больше функций?
Особая благодарность Джастину Дрейку, Тине Чжэнь и Йоаву Вайсу за отзывы и рецензии.
С самого начала существования проекта Ethereum в нем была сильна философия, заключавшаяся в том, чтобы сделать ядро Ethereum как можно более простым, и как можно больше сделать за счет создания протоколов поверх него.
В блокчейн-пространстве дискуссия "сделать это на L1" и "сосредоточиться на L2" обычно воспринимается в первую очередь как дискуссия о масштабировании, но в действительности аналогичные проблемы существуют для удовлетворения многих потребностей пользователей Ethereum: обмен цифровыми активами, конфиденциальность, имена пользователей, продвинутая криптография, безопасность учетных записей, устойчивость к цензуре, защита от опережения и т.д. Список можно продолжать. Однако в последнее время наблюдается осторожный интерес к желанию закрепить большее количество этих функций в основном протоколе Ethereum.
В этой статье мы рассмотрим некоторые философские рассуждения, лежащие в основе первоначальной философии минимализма, а также некие более современные способы осмысления некоторых из этих идей. Задача состоит в том, чтобы создать основу для более точного определения возможных целей, для которых стоит рассмотреть возможность закрепления определенных функций в протоколе.
Ранняя философия минимализма протокола.
В самом начале истории того, что тогда называлось "Ethereum 2.0", было сильное желание создать чистый, простой и красивый протокол, который старался бы делать как можно меньше сам и почти все оставлял бы на усмотрение пользователей. В идеале протокол должен был представлять собой виртуальную машину, а проверка блока - всего лишь один вызов виртуальной машины.
"Функция перехода в состояние" (функция, обрабатывающая блок) будет представлять собой всего лишь один вызов виртуальной машины, а вся остальная логика будет осуществляться через контракты: несколько контрактов системного уровня, но в основном контракты, предоставляемые пользователями. Одна из очень приятных особенностей этой модели заключается в том, что даже весь хардфорк может быть описан как одна транзакция для контракта процессора блока, которая будет одобрена через управление вне или внутри сети и затем запущена с повышенными правами.
Эти дискуссии в 2015 году в особенности касались двух областей, о которых мы думали: абстракция учетных записей и масштабирование. В случае масштабирования идея заключалась в том, чтобы попытаться создать максимально абстрагированную форму масштабирования, которая выглядела бы как естественное продолжение приведенной выше диаграммы. Контракт мог бы обращаться к части данных, которая не хранится на большинстве узлов Ethereum, а протокол обнаруживал бы это и разрешал вызов через некую очень общую функциональность масштабирования вычислений. С точки зрения виртуальной машины, вызов уйдет в какую-то отдельную подсистему, а затем через некоторое время волшебным образом вернется с правильным ответом.
Этот вариант был рассмотрен вкратце, но вскоре мы от него отказались, поскольку были слишком озабочены проверкой возможности масштабирования блокчейна в любом виде. Хотя, как мы увидим далее, сочетание выборки доступности данных и ZK-EVM означает, что одно из возможных будущих решений для масштабирования Ethereum может оказаться удивительно близким к этому видению! Что касается абстракции учетной записи, то мы с самого начала знали, что какая-то реализация возможна, и поэтому сразу же начали исследования, чтобы попытаться воплотить в реальность что-то максимально близкое к пуристской отправной точке "транзакция - это просто звонок" в реальность.
Одна из основных частей кода здесь - validate_transaction(state, tx), которая проверяет правильность одноразового числа и подписи транзакции. Практическая цель абстракции учетной записи с самого начала заключалась в том, чтобы позволить пользователю заменить базовое увеличение одноразового числа и ECDSA на собственную логику проверки, чтобы пользователи могли легче использовать такие вещи, как функция социального восстановления и кошельки с мультиподписью. Таким образом, поиск способа реорганизации apply_transaction в простой вызов EVM был не просто задачей "сделать код чистым ради чистоты кода", а переносом логики в код учетной записи пользователя, чтобы предоставить пользователям необходимую гибкость.
Однако настойчивое стремление сделать так, чтобы apply_transaction содержал как можно меньше закрепленной логики, привело к возникновению множества проблем. Чтобы понять, почему, давайте рассмотрим одно из самых ранних предложений по абстракции счета - EIP 86:
Спецификация.
Если block.number >= METROPOLIS_FORK_BLKNUM, то: 1. Если подпись транзакции равна (0, 0, 0) (т.е. v = r = s = 0), то считать ее действительной и установить адрес отправителя 2**160 - 1. 2. Установить адрес любого контракта, созданного в результате транзакции создания, равным sha3(0 + init code) % 2**160, где + представляет собой конкатенацию, заменив прежнюю формулу адреса sha3(rlp.encode([sender, nonce])). 3. Создать новый опкод по адресу 0xfb, CREATE_P2SH, который устанавливает адрес создания в sha3(sender + init code) % 2**160. Если контракт по этому адресу уже существует, то происходит сбой и возвращается 0, как если бы у кода init закончился газ.
В принципе, если подпись установлена на (0, 0, 0), то транзакция действительно становится "просто вызовом". Сама учетная запись будет отвечать за наличие кода, который анализирует транзакцию, извлекает и проверяет подпись и одноразовое число, а также оплачивает газ. Ранний пример такого кода см. здесь, а очень похожий код validate_transaction, который этот код учетной записи заменит, см. здесь.
В обмен на эту простоту на уровне протокола майнеры (или, как сегодня говорят, провайдеры блоков) получают дополнительную ответственность за выполнение дополнительной логики, позволяющей принимать и пересылать транзакции только на те учетные записи, код которых настроен на реальную оплату газа. Что это за логика? Честно говоря, в EIP-86 об этом не слишком задумывались:
Обратите внимание, что майнеры должны иметь стратегию приема таких транзакций. Эта стратегия должна быть очень разборчивой, поскольку в противном случае они рискуют принимать транзакции для кода validate_transaction (который этот предварительный код учетной записи будет заменять), которые не будут платить им никаких комиссий, и, возможно, даже транзакции, которые не имеют никакого эффекта (например, потому что транзакция уже была включена и поэтому одноразовое число уже неактуально). Один из простых подходов заключается в том, чтобы иметь белый список для кодового хэша учетных записей, на которые разрешается отправлять транзакции; одобренный код будет включать логику, которая выплачивает майнерам комиссионные за транзакции. Однако такой подход, пожалуй, является слишком ограничительным; более мягкой, но все же эффективной стратегией было бы принятие любого кода, который соответствует общему формату, описанному выше, потребляя лишь ограниченное количество газа для выполнения проверки одноразового числа и подписи и имея гарантию того, что плата за транзакции майнеру будет осуществлена. Другая стратегия заключается в том, чтобы, наряду с другими подходами, пытаться обрабатывать любую транзакцию, запрашивающую менее 250 000 газа, и включать ее только в том случае, если баланс майнера после выполнения транзакции будет соответственно выше, чем до нее. validate_transaction
Если бы EIP-86 был включен как есть, это уменьшило бы сложность EVM, но при этом значительно увеличило бы сложность других частей стека Ethereum, потребовало бы написания по сути точно такого же кода в других местах, а также ввело бы совершенно новые типы странностей, такие как возможность появления одной и той же транзакции с одним и тем же хэшем несколько раз в цепи, не говоря уже о проблеме мульти-инвалидации.
В дальнейшем абстракция учетных записей развивалась поэтапно. EIP-86 превратился в EIP-208, который впоследствии стал постом на ethresear.ch о "компромиссах в предложениях по абстракции учетных записей", который через полгода превратился в этот пост на ethresear.ch. В конце концов, из всего этого получился реально работающий EIP-2938.
Однако EIP-2938 вовсе не был минималистичным. В состав EIP входят:
- Новый тип транзакции.
- Три новые глобальные переменные для всей транзакции.
- Два новых опкода, включая очень громоздкий опкод PAYGAS, который одновременно выполняет проверку цены на газ и лимита газа, является точкой прерывания выполнения EVM и временно хранит ETH для оплаты комиссий. PAYGAS
- Набор сложных стратегий майнинга и ретрансляции, включая список запрещенных опкодов для этапа валидации транзакции.
Для того чтобы запустить абстракцию учетных записей в работу без привлечения разработчиков ядра Ethereum, которые были заняты героическими усилиями по оптимизации клиентов Ethereum и реализации слияния, EIP-2938 в итоге был перестроен на полностью внепротокольный ERC-4337.
Поскольку это ERC, он не требует хардфорка и технически живет "вне протокола Ethereum". Так.... проблема решена? Как оказалось, не совсем. Текущая среднесрочная дорожная карта для ERC-4337 действительно предполагает превращение значительной части ERC-4337 в ряд протокольных функций, и это полезный поучительный пример, позволяющий увидеть причины, по которым такой путь рассматривается.
Закрепление ERC-4337.
Обсуждалось несколько основных причин, по которым ERC-4337 может быть возвращен в протокол:
- Эффективность использования газа: все, что делается внутри EVM, влечет за собой определенный уровень накладных расходов на виртуальную машину, включая неэффективность использования таких дорогостоящих функций, как слоты для хранения данных. В настоящее время эти дополнительные неэффективные затраты составляют не менее ~20 000 газа, а зачастую и больше. Внедрение этих компонентов в протокол - самый простой способ устранить эти проблемы.
- Риск ошибок в коде: если в "контракте точки входа" ERC-4337 будет обнаружена достаточно серьезная ошибка, то все ERC-4337-совместимые кошельки могут лишиться всех своих средств. Замена контракта на встроенную функциональность создает подразумеваемую ответственность за исправление ошибок в коде при хардфорке, что снимает риск потери средств для пользователей.
- Поддержка опкодов EVM, таких как tx.origin. ERC-4337 сам по себе заставляет tx.origin возвращать адрес "сборщика", который упаковал набор пользовательских операций в транзакцию. Нативная абстракция учетной записи может исправить эту ситуацию, заставив tx.origin указывать на реальную учетную запись, отправляющую транзакцию, что позволит работать так же, как и в случае с EOA. tx.origin
- Устойчивость к цензуре: одна из проблем разделения провайдеров и разработчиков заключается в том, что становится проще подвергать цензуре отдельные транзакции. В мире, где отдельные транзакции читаемы для протокола Ethereum, эта проблема может быть значительно смягчена с помощью списков включения, которые позволяют предлагающим указать список транзакций, которые должны быть включены в следующие два слота почти во всех случаях. Однако внепротокольный ERC-4337 оборачивает "пользовательские операции" внутри одной транзакции, делая пользовательские операции непрозрачными для протокола Ethereum; следовательно, списки включения, предоставляемые протоколом Ethereum, не смогут обеспечить устойчивость к цензуре для пользовательских операций ERC-4337. Закрепление ERC-4337 и превращение пользовательских операций в "правильный" тип транзакций позволило бы решить эту проблему.
Стоит подробнее остановиться на вопросе эффективности газа. В своем нынешнем виде ERC-4337 значительно дороже "базовой" транзакции Ethereum: транзакция стоит 21 000 газа, тогда как ERC-4337 - ~42 000 газа. В этом документе перечислены некоторые причины:
- Необходимость оплачивать множество отдельных расходов на чтение/запись в хранилище, которые в случае EOA объединяются в один платеж в 21000 газа:
- Редактирование слота хранилища, содержащего pubkey+nonce (~5000)
- Затраты на вызов UserOperation (~4500, при сжатии уменьшаются до ~2500)
- ECRECOVER (~3000)
- Прогрев самого кошелька (~2600)
- Прогрев аккаунта получателя (~2600)
- Перевод ETH на счет получателя (~9000)
- Редактирование хранилища для оплаты комиссии (~5000)
- Доступ к слоту хранилища, содержащему прокси (~2100), а затем и к самому прокси (~2600) - Помимо вышеуказанных затрат на чтение/запись хранилища, контракт должен выполнять "бизнес-логику" (распаковка UserOperation, ее хеширование, перетасовка переменных и т.д.), которую транзакции EOA выполняют "бесплатно" по протоколу Ethereum
- Необходимость расходовать газ для оплаты логов (EOA не выпускают логов)
- Единовременные затраты на создание контракта (~32000 базовых, плюс 200 газа на каждый байт кода в прокси, плюс 20000 на установку адреса прокси)
Теоретически можно было бы изменить систему затрат на газ EVM так, чтобы внутрипротокольные и внепротокольные затраты на доступ к хранилищу совпадали; нет никаких причин, почему перевод ETH должен стоить 9000 газа, когда другие виды операций по редактированию хранилища гораздо дешевле. И действительно, два EIP ([1] [2]), связанные с предстоящим переходом к дереву Веркла, пытаются это сделать. Но даже если мы это сделаем, есть одна веская причина, по которой закрепленные функции протокола неизбежно будут значительно дешевле кода EVM, независимо от того, насколько эффективным станет EVM: закрепленному коду не нужно платить за газ за то, что он предварительно загружен.
Полнофункциональные кошельки ERC-4337 имеют большие размеры. Данная реализация, скомпилированная и помещенная в цепочку, занимает ~12 800 байт. Конечно, можно развернуть этот большой фрагмент кода один раз и использовать DELEGATECALL, чтобы позволить каждому отдельному кошельку обращаться к нему, но к этому коду все равно нужно обращаться в каждом блоке, который его использует. В соответствии с затратами на газ EIP в дереве Веркла 12 800 байт составят 413 блоков, и доступ к этим блокам потребует оплаты 2x WITNESS_BRANCH_COST (всего 3800 газа) и 413x WITNESS_CHUNK_COST (всего 82 600 газа). И это еще не говоря о самой точке входа ERC-4337, имеющей в версии 0.6.0 23 689 байт ончейн (по правилам EIP дерева Веркла, ~158 700 газа для загрузки).DELEGATECALLWITNESS_BRANCH_COSTWITNESS_CHUNK_COST
Это приводит к проблеме: затраты на газ для фактического доступа к этому коду необходимо как-то разделить между транзакциями. Текущий подход, используемый в ERC-4337, не очень хорош: первая транзакция в пачке съедает разовые затраты на хранение/чтение кода, что делает ее гораздо более дорогой, чем остальные транзакции. Закрепление в протоколе позволило бы этим часто используемым библиотекам просто стать частью протокола, доступной для всех без какой-либо платы.
Что мы можем узнать из этого примера о том, когда следует закреплять те или иные аспекты в более общем виде?
В этом примере мы увидели несколько различных обоснований для закрепления в протоколе аспектов абстракции учетных записей.
- Рыночные подходы, основанные на "перемещении сложности на периферию", в наибольшей степени разрушаются при наличии высоких постоянных затрат. И действительно, долгосрочная дорожная карта абстракции аккаунта, похоже, будет иметь много постоянных затрат на блок. 244 100 газа на загрузку стандартизированного кода кошелька - это одно, но агрегация (см. мою презентацию этим летом) потенциально добавляет еще сотни тысяч газа на проверку ZK-SNARK плюс затраты на проверку доказательств ончейн. Не существует способа взимать плату с пользователей за эти расходы, не создавая при этом большой рыночной неэффективности, в то время как превращение некоторых из этих функций в функции протокола, доступные для всех без взимания платы, полностью решает эту проблему.
- Реакция всего сообщества на ошибки в коде. Если некоторый набор фрагментов кода используется всеми пользователями или очень широким кругом пользователей, то зачастую сообществу блокчейна имеет смысл взять на себя ответственность за хардфорк для исправления возникающих ошибок. В ERC-4337 было представлено большое количество глобально разделяемого кода, и в долгосрочной перспективе более разумно исправлять ошибки в этом коде с помощью хардфорков, чем приводить к потере пользователями большого количества ETH.
- Иногда более сильная форма функции может быть реализована путем непосредственного использования возможностей протокола. Ключевым примером здесь являются внутрипротокольные функции противодействия цензуре, такие как списки включения: внутрипротокольные списки включения могут гарантировать более высокую степень противодействия цензуре, чем внепротокольные подходы, но для того, чтобы операции на уровне пользователя действительно могли воспользоваться внутрипротокольными списками включения, отдельные операции на уровне пользователя должны быть "читаемы" для протокола. Другой менее известный пример: в проектах Ethereum proof of stake образца 2017 года была предусмотрена абстракция учетной записи для ключей стейкинга, но от нее отказались в пользу закрепления BLS, поскольку BLS поддерживает механизм "агрегации", который необходимо было реализовать на уровне протокола и сети, что могло бы сделать работу с очень большим количеством подписей гораздо более эффективной.
Однако важно помнить, что даже закрепленная внутрипротокольная абстракция учетной записи - это все равно масштабное "ослабление закрепления" по сравнению со статус-кво. Сегодня транзакции верхнего уровня в Ethereum могут быть инициированы только с внешних учетных записей (EOA), которые используют для верификации единственную подпись secp256k1 на эллиптической кривой. Абстракция учетной записи устраняет эту проблему и оставляет условия верификации открытыми для пользователей. Таким образом, в этой истории об абстракции учетных записей мы увидели и самый главный аргумент против закрепления: гибкость к различным потребностям пользователей.
Попробуем дополнить эту историю, рассмотрев еще несколько примеров особенностей, которые в последнее время рассматриваются на предмет закрепления. Особое внимание мы уделим: ZK-EVMs, разделение proposer-builder, приватные mempools, liquid staking и новые прекомпиляции.
Закрепление ZK-EVM.
Переключим внимание на другую потенциальную цель для закрепления в протоколе Ethereum: ZK-EVMs. В настоящее время существует большое количество ZK-роллапов, которые должны писать достаточно похожий код для проверки выполнения блоков, подобных Ethereum, внутри ZK-SNARK. Существует довольно разнообразная экосистема независимых реализаций: PSE ZK-EVM, Kakarot, Polygon ZK-EVM, Linea, Zeth, и список можно продолжать.
Одно из последних противоречий в пространстве EVM ZK-rollup связано с тем, как бороться с возможными ошибками в ZK-коде. В настоящее время все существующие системы имеют некий механизм "совета безопасности", который может отменить действие системы доказательства в случае обнаружения ошибки. В этом прошлогоднем посте я попытался создать стандартизированную структуру, чтобы побудить проекты четко определить, какой уровень доверия они оказывают системе доказательства, а какой - совету безопасности, и двигаться в направлении предоставления совету безопасности все меньших полномочий с течением времени.
Однако есть ощущение, что часть этой работы является излишней. У нас уже есть базовый уровень Ethereum, который имеет EVM, и уже есть работающий механизм устранения ошибок в реализации: если есть ошибка, то клиенты, у которых есть ошибка, обновляются, исправляя ее, и цепочка продолжается. Блоки, которые с точки зрения клиента с ошибкой казались завершенными, в итоге перестанут быть завершенными, но, по крайней мере, пользователи не будут терять средства. Аналогичным образом, если роллап просто хочет быть и оставаться EVM-эквивалентным, кажется неправильным, что он должен внедрять собственное управление, чтобы постоянно менять свои внутренние правила ZK-EVM в соответствии с обновлениями базового слоя Ethereum, когда в конечном итоге он строится поверх самого базового слоя Ethereum, который знает, когда он обновляется и по каким новым правилам.
Поскольку эти L2 ZK-EVM, по сути, используют точно такой же EVM, как и Ethereum, не можем ли мы каким-то образом сделать "проверку выполнения EVM в ZK" функцией протокола и решать исключительные ситуации, такие как ошибки и обновления, просто применяя социальный консенсус Ethereum, как мы уже делаем это для выполнения EVM базового слоя?
Это важная и сложная тема. Здесь есть несколько нюансов:
1. Мы хотим быть совместимыми с философией мультиклиента Ethereum. Это означает, что мы хотим позволить разным клиентам использовать разные системы доказательства. Это, в свою очередь, означает, что для любого выполнения EVM, которое было доказано одной системой ZK-SNARK, мы хотим получить гарантию того, что базовые данные доступны, чтобы можно было генерировать доказательства для других систем ZK-SNARK.
2. Пока технология незрелая, мы, вероятно, хотим получить возможность проверяемости. На практике это означает то же самое: если какое-либо выполнение будет доказано, мы хотим, чтобы базовые данные были доступны, чтобы, если что-то пойдет не так, пользователи и разработчики могли это проверить.
3. Нам нужно гораздо более быстрое время доказательства, чтобы при наличии одного типа доказательства другие типы доказательств генерировались достаточно быстро, чтобы другие клиенты могли их проверить. Это можно было бы обойти, сделав прекомпиляцию с асинхронным ответом через некоторый промежуток времени, превышающий слот (например, 3 часа), но это усложняет задачу.
4. Мы хотим поддерживать не только копии EVM, но и "почти-EVM". Отчасти L2 привлекает возможность инноваций на уровне выполнения и делать расширения для EVM. Если виртуальная машина данного L2 отличается от EVM лишь незначительно, было бы неплохо, если бы L2 мог использовать собственный внутрипротокольный ZK-EVM для тех частей, которые идентичны EVM, и полагаться на собственный код для тех частей, которые отличаются. Это можно сделать, разработав прекомпиляцию ZK-EVM таким образом, чтобы она позволяла вызывающей стороне указать битовое поле или список опкодов или адресов, которые будут обрабатываться не самим EVM, а таблицей, поставляемой извне. Можно также сделать расходы на газ открытыми для настройки в ограниченном объеме.
Одним из возможных спорных вопросов, связанных с доступностью данных в "родном" ZK-EVM, является отслеживаемость состояния. ZK-EVM гораздо более эффективны, если они не должны нести "свидетельские" данные. То есть, если какой-то фрагмент данных уже был прочитан или записан в каком-то предыдущем блоке, то можно просто считать, что у проверяющих есть к нему доступ, и не делать его снова доступным. Это не просто отсутствие необходимости повторной загрузки хранилища и кода: оказывается, что при правильном сжатии данных в роллапах сжатие с учетом сохранения состояния позволяет сэкономить до 3 раз по сравнению со сжатием без сохранения состояния.
Это означает, что для прекомпиляции ZK-EVM у нас есть два варианта:
1. Прекомпиляция требует, чтобы все данные были доступны в одном блоке. Это означает, что проверки могут быть без сохранения состояния, но это также означает, что ZK-роллапы, использующие такую прекомпиляцию, становятся намного дороже, чем роллапы, использующие пользовательский код.
2. Прекомпиляция позволяет указывать на данные, использованные или сгенерированные предыдущими выполнениями. Это позволяет приблизить ZK-роллапы к оптимальным, но это сложнее и вводит новый вид состояния, которое должно храниться проверяющими.
Какие уроки мы можем извлечь из этого? Есть достаточно веские аргументы в пользу того, чтобы каким-то образом закрепить валидацию ZK-EVM: роллапы уже создают свои собственные версии, и кажется неправильным, что Ethereum готов использовать вес своих многочисленных реализаций и внецепочечного социального консенсуса для выполнения EVM на L1, а L2, выполняющие точно такую же работу, вынуждены вместо этого реализовывать сложные гаджеты с участием советов по безопасности. Но, с другой стороны, в деталях кроется дьявол: существуют различные версии закрепленной ZK-EVM, которые имеют различные затраты и преимущества. Разделение на "сохранение состояния" и "без сохранения состояния" - это лишь поверхностный взгляд на проблему; попытка поддержки "почти-EVM" с пользовательским кодом, проверенным другими системами, скорее всего, откроет еще большее пространство для проектирования. Таким образом, закрепление ZK-EVM несет в себе как перспективы, так и проблемы.
Закрепление разделения на провайдера и строителя (ePBS).
С появлением MEV добыча блоков превратилась в деятельность, требующую больших масштабов, причем искушенные участники могут создавать блоки, приносящие гораздо больший доход, чем стандартные алгоритмы, которые просто следят за транзакциями в мемпуле и включают их. До сих пор сообщество Ethereum пыталось решить эту проблему с помощью внепротокольных схем разделения провайдеров и строителей, таких как MEV-Boost, которые позволяют обычным валидаторам ("провайдерам") передавать создание блоков специализированным участникам ("строителям").
Однако MEV-Boost несет в себе предположение о доверии к новой категории участников, называемой ретранслятором. В последние два года появилось много предложений по созданию "закрепленных PBS". В чем выгода от этого? В данном случае ответ довольно прост: PBS, которую можно построить, непосредственно используя возможности протокола, просто сильнее (в смысле наличия более слабых предположений о доверии), чем PBS, которую можно построить без них. Это похоже на случай закрепления внутрипротокольных ценовых оракулов - хотя в этой ситуации также существует сильный контраргумент.
Закрепление приватных mempool'ов.
Когда пользователь отправляет транзакцию, она сразу же становится публичной и видимой для всех, даже до того, как попадает в цепочку. Это делает пользователей многих приложений уязвимыми для экономических атак, таких как фронтраннинг: если пользователь совершает крупную сделку, например, на Uniswap, злоумышленник может провести транзакцию прямо перед ним, увеличив цену покупки и получив арбитражную прибыль.
В последнее время появился ряд проектов, специализирующихся на создании "приватных мемпулов" (или "зашифрованных мемпулов"), в которых транзакции пользователей хранятся в зашифрованном виде до момента их необратимого принятия в блок.
Однако проблема заключается в том, что подобные схемы требуют особого типа шифрования: чтобы пользователи не наводняли систему и не опережали сам процесс дешифрования, шифрование должно автоматически расшифровываться, как только транзакция действительно будет необратимо принята.
Для реализации такой формы шифрования существуют различные технологии с разными компромиссами, которые хорошо описаны в этом посте Джона Шарбонно (а также в этом видео и слайдах):
- Шифрование для централизованного оператора, например, Flashbots Protect.
- Шифрование с временной блокировкой - форма шифрования, которая может быть расшифрована кем угодно после определенного количества последовательных вычислительных шагов, которые не могут быть распараллелены.
- Пороговое шифрование, доверяющее расшифровку данных честному комитету большинства. Ознакомьтесь с концепцией цепи маяков с закрытыми шторами для конкретного предложения.
- Надежное оборудование, такое как SGX.
К сожалению, каждый из этих способов имеет свои недостатки. Централизованный оператор не может быть включен в протокол по очевидным причинам. Традиционное шифрование с временной блокировкой слишком дорого для выполнения тысяч транзакций в публичном мемпуле. Более мощный примитив, называемый отложенным шифрованием, позволяет эффективно расшифровывать неограниченное число сообщений, но на практике его трудно построить, а атаки на существующие конструкции все же иногда обнаруживаются. Как и в случае с хэш-функциями, нам, вероятно, потребуется еще несколько лет исследований и анализа, прежде чем отложенное шифрование станет достаточно зрелым. Пороговое шифрование требует, чтобы большинство не вступало в сговор, в условиях, когда они могут вступать в сговор незаметно (в отличие от атак 51%, где сразу очевидно, кто участвовал). SGX создает зависимость от одного надежного производителя.
Хотя для каждого решения существует некоторое подмножество пользователей, которым удобно доверять ему, не существует единого решения, которому можно было бы доверять настолько, чтобы его можно было практически принять на уровне 1. Таким образом, закрепление защиты от обхода на уровне 1 представляется затруднительным, по крайней мере, до тех пор, пока не будет усовершенствована задержка шифрования или не произойдет какой-либо другой технологический прорыв, хотя это достаточно ценная функциональность, чтобы уже сейчас появилось множество прикладных решений.
Закрепление ликвидного стейкинга.
Среди пользователей Ethereum defi часто встречается запрос на возможность использовать свои ETH для стейкинга и одновременно в других приложениях. Другим распространенным требованием является простое удобство: пользователи хотят иметь возможность стейкать без сложностей, связанных с запуском узла и его постоянным нахождением в сети (и защитой своих ключей стейкинга, которые теперь находятся в сети).
На сегодняшний день самым простым "интерфейсом", удовлетворяющим обе эти потребности, является токен ERC20: конвертируйте свой ETH в "staked ETH", держите его, а затем конвертируйте обратно. И действительно, появились провайдеры ликвидного стейкинга, такие как Lido и Rocketpool, которые занимаются именно этим. Однако в ликвидном стейкинге действует естественная централизующая механика: люди, естественно, идут в самую большую версию стейкинга ETH, потому что она наиболее привычна и наиболее ликвидна (и наиболее хорошо поддерживается приложениями, которые, в свою очередь, поддерживают ее, потому что она более привычна и потому что о ней слышало большинство пользователей).
Каждая версия стейкинга ETH должна иметь определенный механизм, определяющий, кто может быть оператором базового узла. Он не может быть неограниченным, так как в этом случае злоумышленники будут присоединяться и усиливать свои атаки за счет средств пользователей. В настоящее время наиболее популярными являются Lido, в которой существует DAO, ведущая белый список операторов узлов, и Rocket Pool, позволяющая любому желающему управлять узлом, если он внесет в качестве депозита 8 ETH (т.е. 1/4 капитала). Эти два подхода имеют разные риски: подход Rocket Pool позволяет злоумышленникам атаковать сеть на 51% и заставляет пользователей оплачивать большую часть расходов. При подходе DAO, если один такой стабфонд доминирует, это приводит к тому, что один, потенциально атакуемый управляющий гаджет контролирует очень большую часть всех валидаторов Ethereum. К чести протоколов типа Lido, они реализовали защиту от этого, но одного уровня защиты может оказаться недостаточно.
В краткосрочной перспективе одним из вариантов является социальное стимулирование участников экосистемы к использованию различных провайдеров ликвидного стейкинга, чтобы снизить вероятность того, что какой-либо один из них станет слишком крупным и будет представлять системный риск. Однако в долгосрочной перспективе такое равновесие неустойчиво, и слишком полагаться на моральное давление при решении проблем опасно. Возникает естественный вопрос: может быть, имеет смысл закрепить в протоколе некую функциональность, чтобы сделать ликвидный стейкинг менее централизованным?
Здесь ключевым является вопрос: какого рода функциональность внутри протокола? Простое создание в рамках протокола взаимозаменяемого токена "staked ETH" сопряжено с проблемой, которая заключается в том, что он должен либо иметь закрепленное в рамках всего Ethereum управление для выбора управляющих узлов, либо быть открытым для входа, что превращает его в средство для злоумышленников.
Интересной идеей являются труды Данкрада Фейста о максимализме ликвидного стейкинга. Во-первых, мы исходим из того, что если Ethereum будет атакован на 51%, то, возможно, только 5% атакующих ETH будут вырезаны. Это разумный компромисс. Сейчас в стейкинге находится более 26 млн. ETH, и стоимость атаки в 1/3 от этой суммы (~8 млн. ETH) является чрезмерной, особенно если учесть, что многие виды атак "вне модели" могут быть проведены за гораздо меньшие деньги. Действительно, подобный компромисс уже рассматривался в предложении "суперкомитета" по внедрению финализации одного слота.
Если мы примем, что только 5% атакующих ETH будут порезаны, то более 90% зайстейканных ETH будут неуязвимы для разрушения, и, таким образом, 90% застейканных ETH могут быть помещены во внутрипротокольный взаимозаменяемый токен ликвидного стейкинга, который затем может быть использован в других приложениях.
Этот путь интересен. Но остается открытым вопрос: что конкретно будет закреплено? RocketPool уже работает примерно так: каждый оператор узла вносит определенный капитал, а ликвидные стейкеры вносят остальное. Мы можем просто подправить несколько констант, ограничив максимальный штраф за слеш, например, 2 ETH, и существующие в Rocket Pool rETH станут безрисковыми.
Есть и другие умные вещи, которые можно сделать с помощью простых изменений в протоколе. Например, представим, что мы хотим создать систему, в которой есть два "уровня" стейкинга: операторы узлов (высокие требования к обеспечению) и вкладчики (без минимума, можно присоединяться и выходить в любое время), но при этом мы хотим защитить от централизации оператора узла, предоставив случайно выбранному комитету вкладчиков такие полномочия, как предложение списков транзакций, которые должны быть включены (в целях борьбы с цензурой), контроль выбора форка во время утечки неактивности или необходимость подписывать блоки. Это можно сделать в основном внепротокольным способом, изменив протокол таким образом, чтобы каждый валидатор предоставлял (i) обычный ключ стейкинга и (ii) ETH-адрес, который может быть вызван для вывода вторичного ключа стейкинга во время каждого слота. Протокол будет наделять полномочиями эти два ключа, но механизм выбора второго ключа в каждом слоте можно оставить на усмотрение протоколов пула стейкинга. Возможно, некоторые вещи все же лучше закрепить напрямую, но важно отметить, что такое "закрепление одних вещей, оставление других на усмотрение пользователей" существует.
Закрепление большего количества предварительных компиляций.
Прекомпиляции (или "предварительно скомпилированные контракты") - это контракты Ethereum, реализующие сложные криптографические операции, логика которых изначально реализована в клиентском коде, а не в коде смарт-контракта EVM. Прекомпиляция была компромиссом, принятым в начале развития Ethereum: поскольку накладные расходы на виртуальную машину слишком велики для определенных видов очень сложного и узкоспециализированного кода, мы можем реализовать несколько ключевых операций, ценных для важных типов приложений, в родном коде, чтобы сделать их быстрее. Сегодня это, в основном, несколько специфических хэш-функций и операций с эллиптическими кривыми.
В настоящее время идет активная работа по добавлению прекомпиляции для secp256r1 - эллиптической кривой, несколько отличающейся от secp256k1, используемой для базовых учетных записей Ethereum, поскольку она хорошо поддерживается доверенными аппаратными модулями, а значит, ее широкое использование может повысить безопасность кошельков. В последние годы также предпринимались попытки добавить прекомпиляции для BLS-12-377, BW6-761, обобщенных пар и других возможностей.
Контраргументом против таких запросов на увеличение количества прекомпиляций является то, что многие из прекомпиляций, которые были добавлены ранее (например, RIPEMD и BLAKE), в итоге использовались гораздо реже, чем предполагалось, и нам следует извлечь из этого урок. Вместо того чтобы добавлять новые прекомпиляции для конкретных операций, нам, возможно, следует сосредоточиться на более умеренном подходе, основанном на таких идеях, как EVM-MAX и неактуальное, но все еще сохраняющееся предложение SIMD, которое позволит реализациям EVM выполнять широкие классы кода с меньшими затратами. Возможно, даже существующие малоиспользуемые прекомпиляции могут быть удалены и заменены (неизбежно менее эффективными) реализациями EVM-кода той же функции. Тем не менее, не исключено, что существуют специфические криптографические операции, которые достаточно ценны для ускорения, чтобы имело смысл добавлять их в качестве прекомпиляций.
Что мы из всего этого извлекаем?
Стремление закрепить как можно меньше информации понятно и хорошо; оно проистекает из традиции философии Unix, которая заключается в создании минималистичного программного обеспечения, легко адаптируемого к различным потребностям пользователей, что позволяет избежать проклятия раздутости программного обеспечения. Однако блокчейн - это не операционная система для персональных компьютеров, а социальная система. Это означает, что для закрепления определенных функций в протоколе есть основания, выходящие за рамки тех, которые существуют в контексте чисто персональных компьютеров.
Во многих случаях эти другие примеры повторяют уроки, схожие с теми, что мы видели в абстракции учетных записей. Но есть и несколько новых уроков:
- Закрепление функций может помочь избежать рисков централизации в других областях стека. Зачастую, если базовый протокол минимален и прост, сложность переносится в некую экосистему вне протокола. С точки зрения философии Unix это хорошо. Однако иногда возникает риск централизации этой экосистемы вне протокола, часто (но не только) из-за высоких постоянных затрат. Иногда закрепление может уменьшить де-факто централизацию.
- Слишком сильное закрепление может привести к чрезмерному увеличению нагрузки на протокол в плане доверия и управления. Это тема предыдущего поста о том, как не перегружать консенсус Ethereum: если закрепление конкретной функции ослабляет модель доверия и делает Ethereum в целом гораздо более "субъективным", это ослабляет доверительную нейтральность Ethereum. В таких случаях лучше оставить эту особенность в качестве механизма поверх Ethereum и не пытаться перенести ее в сам Ethereum. В данном случае зашифрованные мемпулы являются лучшим примером того, что может быть слишком сложно закрепить, по крайней мере, до тех пор, пока не будет улучшена технология шифрования задержки.
- Если закрепить слишком много, это может привести к чрезмерному усложнению протокола. Сложность протокола - это системный риск, а добавление слишком большого количества функций в протокол увеличивает этот риск. Прекомпиляция - лучший пример этого.
- В долгосрочной перспективе закрепление может привести к обратному результату, поскольку потребности пользователей непредсказуемы. Функция, которую многие считают важной и которая будет использоваться многими пользователями, вполне может оказаться малоприменимой на практике.
Кроме того, примеры ликвидного стейкинга, ZK-EVM и прекомпиляции демонстрируют возможность промежуточного пути: минимальное жизнеспособное закрепление. Вместо того чтобы закреплять всю функциональность, протокол может закрепить конкретную часть, которая решает ключевые проблемы, обеспечивающие простоту реализации этой функциональности, не будучи при этом слишком категоричным или узконаправленным. Примерами могут служить:
- Вместо того чтобы закреплять полную систему ликвидного стейкинга, можно изменить правила штрафов за стейкинг, чтобы сделать более жизнеспособными бездоверительный ликвидный стейкинг
- Вместо того чтобы вводить больше прекомпиляций, следует ввести EVM-MAX и/или SIMD, чтобы упростить эффективную реализацию более широкого класса операций
- Вместо того чтобы закреплять всю концепцию роллапов, мы можем просто закрепить верификацию EVM.
Мы можем расширить нашу диаграмму из предыдущего поста следующим образом:
Иногда даже имеет смысл отказаться от некоторых вещей. Одним из примеров является отмена закрепления малоиспользуемых прекомпиляций. Абстракция учетных записей в целом, как уже говорилось, также является важной формой отмены закрепления. Если мы хотим поддержать обратную совместимость для существующих пользователей, то механизм может быть удивительно похож на тот, который используется для удаления прекомпиляций: одним из предложений является EIP-5003, который позволит EOA конвертировать свой счет in-plce в контракт с той же (или лучшей) функциональностью.
Какие функции следует привнести в протокол, а какие оставить для других уровней экосистемы - это сложный компромисс, и мы должны ожидать, что со временем он будет меняться по мере того, как будет улучшаться наше понимание потребностей пользователей и набор доступных идей и технологий.
Оригинал здесь.
Перевод: @True_Market_Vision