Исходный текст: «Тщеславные адреса» от foobar.

Сборник: каша на ночь, путь DeFi

160 миллионов долларов США исчезли, и Wintermute потеряла средства. Wintermute — один из самых проницательных рыночных фондов в отрасли. Однажды сентябрьским утром, когда человек, отвечающий за Wintermute, проснулся, он обнаружил один из своих важных кошельков. потеряли 9-значные средства. Так что же привело к краже Wintermute? Это вызвано плохой случайностью в генераторе тщеславных адресов. Хакеры «черной шляпы» подбирают пару секретного ключа и публичного адреса с нуля, а затем большое количество криптоактивов переводится.

Также есть история о взломе Indexed Finance: в октябре 2021 года у них было украдено 16 миллионов долларов, а затем украденные средства были переведены на адреса, начинающиеся с 0xba5ed… . Чего они не знали, так это того, что на тщеславный адрес также повлияла уязвимость плохой случайности, которая поразила Wintermute, и в сентябре 2022 года все деньги были снова украдены и перешли на другой взломанный адрес кошелька. Воры безжалостны.

Итак, с какими проблемами столкнулись эти талантливые разработчики и чему мы можем у них поучиться?

Слева — обычный адрес, используемый для контракта WETH. Справа красивый адрес с 14 ведущими нулями для оптимизации робота MEV. Самый распространенный тип тщеславного адреса — это адрес с множеством ведущих нулей.

Прежде всего, что такое тщеславный адрес (также известный как тщеславный адрес)? Тщеславный адрес — это публичный адрес, который пользователь намеренно создает для связи со своим кошельком или смарт-контрактом. Возможно, он начинается с 0x0000000, может быть, он начинается с 0xdeadbeef, а может быть, это какой-то другой обычный адрес. Причин их популярности несколько:

1. Оптимизация газа: Wintermute сэкономила 15 000 долларов США, используя адрес EOA с несколькими ведущими нулями. Звучит глупо? Многие согласны, но именно так работает EVM. Если в вашем адресе много нулей, комиссия за транзакцию может снизиться. Поэтому, если вы используете адрес смарт-контракта с большим количеством ведущих нулей, пользователи будут счастливы, взаимодействуя с ним, потому что это экономит им деньги.

В «Желтой книге» Ethereum описывается, как адреса с ведущими нулями могут обеспечить более дешевый газ.

2. Соглашение о бренде. Знаете ли вы, что контракт токена 1inch начинается с 0x111111111...?

1-дюймовый токен-контракт

3. Многонитевая повторяемость. По моему мнению, это главный приоритет и почему каждый протокол должен использовать тщеславный адрес для своего развертывания. Ваше приложение может находиться в 15 различных цепочках EVM и везде иметь один и тот же адрес! Не будет ли это проще для разработчиков и пользователей?

Так когда же красивый адрес безопасен?

Существует два типа адресов Ethereum: внешние учетные записи (EOA) и учетные записи смарт-контрактов. Если вы использовали кошелек, такой как MetaMask, каждый адрес в нем представляет собой EOA, который используется для подписи сообщений и обработки транзакций. Сравните это с учетной записью смарт-контракта, такой как контракт Uniswap, с которым люди могут взаимодействовать, но он не может предпринимать собственные действия без запуска. Подводя итог, можно сказать, что это очень просто. Адреса хороших номеров небезопасны для учетных записей EOA, но безопасны для учетных записей смарт-контрактов.

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

Вот почему Wintermute терпит неудачу, а OpenSea преуспевает: генерировать закрытые ключи в незащищенной памяти с помощью небезопасного программного обеспечения — это плохо. Но генерировать публичные семена таким способом довольно приятно! Поэтому хороший адрес EOA — это путь к банкротству, а хороший адрес смарт-контракта — это путь к успеху.

Почему протокол требует красивого адреса?

Упрощенная документация! Вы можете указать адрес контракта во всех цепочках;

Проверяется пользователем! Тот же адрес контракта появится только тогда и только тогда, когда байт-код совпадает побайтно;

Разработчики могут проверить! Поскольку идентичные адреса контрактов встречаются только в случае точного совпадения, вы можете уловить небольшие хитрые изменения в сценарии развертывания;

Легкая интеграция! Другие протоколы могут жестко закодировать адрес вашего контракта в свой мультичейнкод без необходимости использования операторов if на основе ChainId.

ПРИМЕЧАНИЕ. Мы собираемся углубиться в подробное руководство по эксплуатации. Впервые собрав все воедино, мы углубляемся в техническую область и ориентируемся на разработчиков смарт-контрактов, имеющих опыт развертывания смарт-контрактов в сети. Если вам интересно, продолжайте читать, но если это не для вас, не беспокойтесь о том, чтобы не отставать. В конце вас ждет дополнительное техническое задание (с наградами).

Смарт-контракт красивый адрес

Существует способ генерировать на 100% безопасные адреса смарт-контрактов, независимо от того, какое программное обеспечение вы используете, не имеет значения, если итеративная технология станет публичной утечкой. Он называется «фабричный метод CREATE2», и он не только предоставляет красивый адрес, но и является надежным способом гарантировать, что у вас есть один и тот же адрес развертывания контракта в нескольких цепочках. Это также позволяет другим безопасно развертывать код от вашего имени без какого-либо совместного использования закрытого ключа или предположений о nonce.

Во-первых, краткий обзор того, как выбрать адрес смарт-контракта. Существует два варианта развертывания: CREATE и CREATE2. Когда вы развертываете смарт-контракт непосредственно из EOA, по умолчанию используется процесс CREATE. Адрес определяется путем хеширования адреса создателя контракта с одноразовым номером создателя контракта. Этот nonce указывает на количество транзакций, отправленных адресом, поэтому новый кошелек начинается с 0 и увеличивается на 1 каждый раз, когда отправляется новая транзакция. Вот волшебная формула адреса смарт-контракта, используемая CREATE:

новый_адрес = хэш (отправитель, одноразовый номер)

Менее распространенным, но более интересным является адрес смарт-контракта, развернутый с помощью CREATE2, и вот его формула:

новый_адрес = хэш (0xFF, отправитель, соль, байт-код)

Первое кажется проще, не так ли? Однако давайте приведем пример того, где эта простота может оказаться вредной по сравнению с более надежным процессом CREATE2.

Эйри Алиса: Есть проблема с мультичейном

Представьте, что разработчик криптовалюты по имени Алиса создает два смарт-контракта: форк Uniswap под названием GriddleSwap и проект NFT под названием ph00ts. Все они являются неизменяемыми независимыми примитивами, что означает отсутствие внешних зависимостей или рисков перекрестных мостов. Алиса развертывает GriddleSwap в Ethereum, используя nonce 0, а затем развертывает ph00ts в Ethereum, используя nonce 1. К сожалению, у Алисы нехватка внимания, и она на несколько минут отвлеклась на крипто-Твиттер, прежде чем развернуть свою работу на Binance Smart Chain (BSC), второй по величине платформе смарт-контрактов.

Упс, перепутал порядок развертывания!

Но подождите! Она напутала в порядке развертывания и развернула ph00ts до GriddleSwap. Поскольку адрес смарт-контракта зависит только от адреса создателя, а в развернутой цепочке блоков Ethereum Gridleswap имеет тот же адрес, что и BSC ph00ts! Что еще хуже, адрес Ethereum ph00ts совпадает с адресом BSC GriddleSwap. Считать, что конечные пользователи будут в замешательстве, значит ничего не сказать. Фактически, злонамеренные развертыватели могут использовать его, чтобы заставить людей думать, что поведение контракта в цепочке такое же — что является справедливым предположением, учитывая тот же адрес!

Осторожная Алиса: Проблемы всё равно будут

Даже если Алиса будет осторожна при развертывании и никогда не перепутает порядок своих одноразовых номеров, возникают другие проблемы. Если Алиса правильно развернется в Ethereum и BSC, но затем выполнит несвязанную транзакцию в Polygon, значение nonce 0 будет израсходовано. Она никогда не сможет развернуть там GriddleSwap, поскольку ее значение nonce было увеличено. Поэтому закрытые ключи развертывателя должны быть защищены любой ценой. Если Алиса утаит данные, злоумышленник сможет совершить несвязанные транзакции. Если Алиса потеряет его, она также потеряет возможность снова развернуться по этому адресу в новой цепочке. Это постоянная уязвимость, защита закрытого ключа которой зависит от честного человека. Если даже разработчики ядра Биткойна не могут этого сделать, то как остальные из нас смогут это сделать?

Решение: СОЗДАТЬ2

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

новый_адрес = хэш (0xFF, отправитель, соль, байт-код)

Первый параметр 0xFF — это постоянное значение, которое можно игнорировать. Второй параметр (адрес отправителя) можно сделать согласованным, выбрав развертывание CREATE2Factory z0age 0x0000000000FFe8B47B3e2130213B802212439497 в большинстве цепочек EVM. Третий параметр — это выбираемая пользователем соль, которую мы можем использовать, чтобы найти хороший адрес и затем сохранить его в цепочке неизменным. Четвертый — это байт-код контракта, который служит полезной проверкой работоспособности, чтобы убедиться, что мы развертываем в цепочке точно такие же функции. Все четыре параметра могут оставаться неизменными независимо от того, что делает какой-либо один развертыватель.

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

Более подробный обзор см. в научно-популярной статье OpenZeppelin‌.

Создайте свой красивый адрес

Думаете, Proof of Work (PoW) станет бесполезным после слияния Ethereum? Подумайте еще раз! Те же возможности графического процессора, которые помогают находить прообразы хеша с большим количеством ведущих нулей для блоков Биткойна, также отлично подходят для поиска прообразов хэша с большим количеством ведущих нулей для смарт-контрактов EVM. z0age из OpenSea (спасибо его объяснению к этому посту) нашел простую настройку для создания собственного тщеславного адреса.

1. Используйте обширный.ai‌ для запуска примера экземпляра графического процессора, который выполняет около 2 миллиардов попыток в секунду и стоит около 25 центов в час:

Изображение: nvidia/opencl

Графический процессор: 1x RTX 3090

Необходимое дисковое пространство: 1,83 ГБ.

2. SSH и устанавливаем ржавчину + create2crunch

sudo apt install build-essential -y; curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | ш -с -- -у; источник "$HOME/.cargo/env"; git clone https://github.com/0age/create2crunch && cd create2crunch; sed -i 's/0x4/0x40/g' src/lib.rs

3. Запустите поиск начального числа. Для переменных среды INIT_CODE_HASH — это keccak256 кода создания контракта. Распечатку образца литейного теста можно найти здесь ‌ — обязательно проверьте ее, прежде чем потреблять большие объемы вычислительных ресурсов! LEADING должно быть количеством начальных нулевых байтов, которое вы хотите, а TOTAL должно быть общим количеством нулевых байтов, которые вы хотите включить в адрес контракта.

экспорт FACTORY="0x0000000000ffe8b47b3e2130213b802212439497"; экспорт CALLER="0x0000000000000000000000000000000000000000"; экспорт INIT_CODE_HASH="0xabc...def"; экспорт LEADING=5; экспорт ВСЕГО=7; грузовой пробег --release $FACTORY $CALLER $INIT_CODE_HASH 0 $LEADING $TOTAL

Когда z0age впервые выпустил свой репозиторий, он был способен выполнять 1,9 миллиарда попыток в секунду на вышеупомянутом оборудовании обширного AI. С тех пор векторизация на некоторых ядрах OpenGL сошла с ума, и я наблюдал 2,15 миллиарда попыток в секунду. Это означает, что поиск адреса с 5 ведущими нулевыми байтами займет 256^5/(2150000000 * 60) ~= 8 минут, а поиск адреса с 6 ведущими нулевыми байтами займет 256^6/(2150000000 * 3600) ~ = 36 часов. . Адрес с 7 ведущими нулевыми байтами занимает 256^7/(2150000000 * 86400) ~= 387 дней. Обратите внимание, что один байт равен двум шестнадцатеричным символам, поэтому адрес с 5 ведущими байтами будет иметь 10 нулей. Конечно, этот поиск можно полностью распараллелить, и фактическая вероятность успеха с течением времени будет подчиняться распределению Пуассона.

Развертывание фабрики CREATE2

Внимательные читатели, возможно, заметили, что фабрика CREATE2 уже существует по адресу 0x0000000000FFe8B47B3e2130213B802212439497 во всех цепочках. Это своего рода проблема курицы и яйца: как согласованное развертывание адресов зависит от согласованного развертывания адресов?

Когда я впервые узнал об этом подходе, я подумал, что это просто закрытый ключ, принадлежащий кому-то умнее меня (сценарий «наблюдательной Алисы» выше). Но на самом деле это гораздо более надежный подход! Подход «транзакций без ключа» основателя ENS Ника Джонсона использует тот факт, что вы можете восстановить публичный адрес из любой подписи транзакции, не зная соответствующего секретного ключа, которым она подписана. Следовательно, можно создать транзакцию («развернуть фабрику create2»), а затем изобрести для нее поддельную подпись, например, состоящую только из 2. Закрытый ключ этой поддельной подписи существует, но никто не знает, что это такое. Но мы можем восстановить публичный адрес, соответствующий «подписи без ключа», отправить на него немного ETH, а затем отправить подписанную транзакцию в мемпул. Несмотря на неясность этого метода, это действительная транзакция и фактически единственная действительная транзакция, которую можно отправить с этого публичного адреса.

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

Конкретные адреса и контракты, созданные во время транзакций без ключа.

Это можно сделать с помощью трех простых команд «forge cast». Байт-код слишком длинный, чтобы копировать его здесь, но вы можете следовать инструкциям по адресу https://github.com/ProjectOpenSea/seaport/blob/main/docs/Deployment.md‌ без разрешения в любой цепочке по вашему выбору. Легко разверните CREATE2 Factory!

Разумеется, если он уже был развернут, нет необходимости развертывать его еще раз.

Примечание: требования EIP-155 ужасны.

Краткая попытка поддержать мои приключения в области управления L1, смело пропускайте ее. EIP-155‌ — это предложение, выдвинутое Виталиком в 2016 году, которое вводит концепцию «идентификатора цепочки» для предотвращения атак повторного воспроизведения. Каждая цепочка имеет свой собственный уникальный идентификатор — 1 для Ethereum, 56 для BSC и 137 для Polygon — который будет включен в подписанные транзакции для предотвращения атак повторного воспроизведения, что было быстро принято Ethereum, и все остальные цепочки EVM последовали этому предложению. Это здорово, но проблема возникает, когда выбираются несколько цепочек, таких как Evmos, который недавно решил явно запретить транзакции до EIP-155 ‌, который по странным причинам предотвращает операционную ошибку, когда Optimism отправил 20 миллионов токенов OP на адрес с мультиподписью (да, снова они), которого Wintermute не существует, утверждает, что владеет им, но никогда не был инициализирован. Однако отключение транзакций до 155 существенно нарушит работу целого набора кросс-чейн развертываний, таких как фабрика CREATE2 и ведущие проекты, такие как Seaport. Эти предложения по управлению должны быть немедленно отменены, а подобные детали защиты должны поступать из кошелька, а не из уровня консенсуса. Если будущее за мультичейн, то эти ненужные ограничения станут огромным препятствием для развертывания лучших проектов на вашем блокчейне.

Забавная штука: развертывание наград

Сегодня https://delegate.cash развернут в 7 различных цепочках EVM (Ethereum, Polygon, Optimism, Celo, Avalanche, Fantom и Arbitrum) и 7 тестовых сетях, соответствующих этим цепочкам. Адрес контракта для всех них один и тот же: 0x00000000000076A84feF008CDAbe6409d2FE638B.

Так этого достаточно? Нет, нам нужно больше цепей. Поскольку Delegatecash — это независимый примитив с нулевыми зависимостями, это означает, что риск существования нескольких цепочек фактически равен нулю. Это чистая польза! Таким образом, первым 5 людям, которые развернут и проверят смарт-контракт Delegatecash в новой цепочке и соответствующей тестовой сети, я награжу бонусом в размере 100 долларов США!

Здесь вам необходимо использовать сценарий развертывания из репозитория с открытым исходным кодом. Для этого может потребоваться развертывание фабрики CREATE2, если она не существует, и не забудьте проверку Etherscan! Удачного развертывания и наслаждайтесь обучением на собственном опыте!