EIP-1052: снять отпечаток контракта одним опкодом
Чтобы проверить, выполняет ли другой адрес именно тот байткод, который вы ожидаете, раньше приходилось копировать весь его код в память и хешировать его самостоятельно — операция O(размер кода) только ради 32-байтного отпечатка. EIP-1052 добавляет EXTCODEHASH, который возвращает этот хеш напрямую, за константное время.
- аккаунты и код (EXTCODECOPY)
- хеширование keccak256
Constantinople, февраль 2019. Контракту часто нужно знать, из чего сделан другой аккаунт. Это адрес контракта или обычного кошелька? Выполняет ли он ровно тот байткод, который я аудировал, или что-то незнакомое? Самый чистый способ ответить — с помощью отпечатка: хеша кода аккаунта, который можно сравнить с известным значением. EVM уже умела хешировать и уже умела читать код чужих аккаунтов, но не было способа получить этот отпечаток дёшево: приходилось тащить весь код в память и хешировать его самостоятельно. EIP-1052 добавляет тот единственный опкод, который пропускает эту перевозку. Давайте выведем его.
Проблема: чтобы захешировать код, сначала приходилось копировать его целиком
До EIP-1052 инструментами EVM для изучения кода чужого аккаунта были EXTCODESIZE Возвращает длину в байтах кода по данному адресу. Полезно, но длина — это не идентичность: два разных контракта могут иметь одинаковый размер. (его длина) и EXTCODECOPY Копирует диапазон кода другого аккаунта в память вызывающего. Чтобы получить отпечаток кода, приходилось скопировать его целиком, а затем захешировать копию — стоимость пропорциональна размеру кода, плюс газ на расширение памяти. (его байты). Ни один из них не даёт отпечаток напрямую. Так что для получения keccak256 кода контракта — того, что вам на самом деле нужно для проверки идентичности, — приходилось делать EXTCODECOPY всего кода в собственную память, а затем запускать KECCAK256 над ним. Стоимость масштабируется с размером кода и сверху добавляет газ на расширение памяти — всё ради единственного 32-байтного значения. Для крупного контракта это большая работа ради простого сравнения.
→ Шаг 2: раскрываем хеш кода отдельным опкодом.
EIP-1052: EXTCODEHASH возвращает хеш напрямую
Исправление добавляет EXTCODEHASH Опкод 0x3f. Получив адрес, возвращает keccak256 кода этого аккаунта за константное время — без копирования. Узел может отдать его напрямую, поскольку хеш кода — часть хранимого представления аккаунта. (0x3f): дайте ему адрес, получите обратно keccak256 кода этого аккаунта одной инструкцией за константное время — без копирования, без расширения памяти. Но с ним идут острые углы, которые нужно учитывать, и все они об отсутствии кода. non-existent account Адрес, к которому никогда не прикасались, — ни баланса, ни nonce, ни кода. EXTCODEHASH возвращает для него 0, отличая «здесь ничего нет» от «есть, но без кода». (несуществующий аккаунт) возвращает 0. Аккаунт, который существует, но не имеет кода, — обычный EOA или контракт в процессе конструирования — возвращает empty-code hash keccak256 пустой байтовой строки (0xc5d2460186…) — хеш кода любого существующего аккаунта без развёрнутого кода. EXTCODEHASH возвращает его для EOA, и это отличается от 0, возвращаемого для несуществующих аккаунтов. (хеш пустого кода), keccak256(""). Два разных ответа «нет кода», и правильное использование опкода означает уметь их различать.
→ Шаг 3: что открывает дешёвый отпечаток.
Выигрыш: проверка того, с кем вы разговариваете
Хеш кода за константное время делает практичным целое семейство проверок. Контракт может подтвердить, что контрагент выполняет ровно тот заведомо безопасный байткод, который он ожидает, — прежде чем ему довериться. Он может отличить контракты от EOA (с учётом оговорок про пустой код и конструктор). И это естественно сочетается с CREATE2: развернув контракт по детерминированному адресу, можно вызвать EXTCODEHASH, чтобы подтвердить, что развёрнутый код — именно тот, что задумывался, — реальная проверка безопасности для фабрик и паттернов обновления. То, что раньше было операцией «скопировать и захешировать» за O(размер кода), теперь — один опкод за O(1), достаточно дешёвый, чтобы делать его прямо на горячем пути.