Часть III · Глава 29 из 43 · Shanghai · Capella

EIP-3860: замер кода, который производит код

Байткод рантайма контракта ограничен по размеру с 2016 года — но initcode, который его производит, не был ни ограничен, ни оплачен за O(n)-работу, которую каждый узел выполняет при его обработке. Подсуньте мегабайты initcode — и вся сеть просканирует их почти бесплатно. EIP-3860 ограничивает initcode и берёт плату за слово.

Обновлено 2 июл. 2026 г. · 7 мин
Предполагается
  • создание контрактов (CREATE / CREATE2)
  • газ и замер работы сети

Shanghai, апрель 2023 года. Развёртывание контракта — это действие из двух частей. Вы отправляете небольшую программу — initcode, — и EVM её исполняет; какие бы байты она ни вернула, они становятся постоянным кодом рантайма контракта. С 2016 года код рантайма ограничен 24576 байтами (EIP-170), и вы платите за его хранение. Но сам initcode проскользнул мимо: он никогда не был ограничен по размеру, а O(n)-работа, которую сеть обязана выполнить лишь для того, чтобы обработать его, не была полностью оплачена. Это небольшая трещина — а в газовой экономике неоценённая O(n)-операция — это ровно форма отказа в обслуживании. EIP-3860 её запечатывает. Давайте его выведем.

1 шаг Неоценённое сканирование

Проблема: initcode был не ограничен, а его O(n)-работа недооценена

Когда EVM получает initcode, он не просто его исполняет — сначала он обязан проделать работу, пропорциональную длине initcode, независимо от того, что делает код. Самый наглядный пример — анализ JUMPDEST (JUMPDEST analysis) Перед исполнением байткода EVM сканирует всю программу, чтобы отметить, какие байтовые смещения являются валидными точками перехода (опкоды JUMPDEST). Это O(n) по длине кода, и его нужно выполнить над всем initcode — работу, которую повторяет каждый узел, независимо от того, доработает ли код до конца. : каждый узел сканирует весь initcode, чтобы найти валидные цели перехода. Код рантайма был ограничен и оплачивался, а initcode был не ограничен, и это побайтовое сканирование не было замерено пропорционально его размеру. Так что атакующий мог подсунуть сети мегабайты initcode и заставить каждый узел перемалывать их за плату далеко ниже реальной стоимости — недооценённая O(n)-операция, классическая форма DoS.

создание контракта: выполнить initcode → вернуть runtime-код initcode — без предела, посимвольный скан не оценён рантайм ≤ 24576 каждый узел гоняет анализ JUMPDEST по всему initcode — O(n) атакующий подаёт мегабайты initcode всё это сканирование стоит сети — но почти не тарифицируется недооценённая DoS-атака на создание контрактов
Создание контракта исполняет initcode, чтобы вернуть код рантайма (ограниченный 24576 байтами со времён EIP-170). Но сам initcode был не ограничен, а O(n)-сканирование JUMPDEST, которое каждый узел выполняет над ним, было недооценено — так что огромный initcode заставляет всю сеть работать почти бесплатно. Недооценённый отказ в обслуживании.

→ Шаг 2: берём плату за размер и ограничиваем его.

2 шаг Замер и ограничение

EIP-3860: два газа за слово плюс жёсткий потолок

Решение — тот же принцип, который Ethereum применяет всякий раз, когда находит неоценённую O(n)-работу: сделать стоимость отслеживающей размер. EIP-3860 добавляет плату за слово initcode (initcode word charge) Дополнительные 2 газа за каждое 32-байтовое слово initcode, взимаемые с CREATE, CREATE2 и транзакций создания контрактов. Она напрямую оценивает работу, пропорциональную длине (такую как анализ JUMPDEST), которую сеть выполняет над initcode. в 2 газа за 32-байтовое слово initcode на CREATE, CREATE2 и транзакции создания — так что чем длиннее initcode, тем больше вы платите, ровно соответствуя сканированию. И добавляет ограничение размера initcode (initcode size cap) Жёсткий лимит в 49152 байта (MAX_INITCODE_SIZE = 2 × MAX_CODE_SIZE) на initcode. Превышение немедленно приводит к отказу, ограничивая наихудшую работу, которую может навязать одно создание контракта. в 49152 байта — ровно вдвое больше лимита рантайма, — так что ни одно создание не может быть неограниченно большим. Теперь стоимость масштабируется вместе с работой, а работа ограничена.

EIP-3860: цена initcode по размеру initcode, разбит на 32-байтные слова 32B32B32B32B32B +2 газа за слово — цена следует за сканированием жёсткий лимит: initcode ≤ 49152 Б (2× runtime) нет безлимитного initcode, нет неоценённой O(n)-работы
EIP-3860 взимает 2 газа за каждое 32-байтовое слово initcode, так что стоимость отслеживает сканирование, пропорциональное длине, и ограничивает initcode 49152 байтами (2× лимита рантайма). Никакого неограниченного initcode, никакой неоценённой O(n)-работы — стандартное решение «замерить работу».

→ Шаг 3: создание и рантайм наконец совпадают.

3 шаг Два согласованных лимита

Итог: симметричное, безопасное от DoS создание

С EIP-3860 две половины жизни контракта наконец симметричны: initcode ограничен 49152 байтами и замерен по слову; код рантайма ограничен 24576 байтами (EIP-170) и оплачивается при депонировании. Создание контракта безопасно от DoS — никакого неограниченного ввода, никакой неоценённой O(n)-работы. Изменение почти незаметно для обычных развёртываний и слегка повышает стоимость паттернов фабрика CREATE2 (CREATE2 factory) Контракт, разворачивающий другие контракты через CREATE2, часто по детерминированным адресам. Поскольку такие паттерны разворачивают множество контрактов, новая плата за слово initcode заметнее всего именно здесь. , разворачивающих контракты массово. Это небольшой EIP с ясной родословной: тот же урок, что и EIP-150 — когда сеть выполняет работу, пропорциональную вашему вводу, вы обязаны платить пропорционально.

два симметричных лимита — оба считаются в газе initcode ≤ 49152 · +2/word runtime (EIP-170) ≤ 24576 создание защищено от DoS · фабрики CREATE2 платят за слова тот же урок «мерить O(n) работу», что и EIP-150
Initcode (≤ 49152 байт, +2 газа/слово) и код рантайма (≤ 24576 байт, EIP-170) теперь согласованная, замеренная пара. Создание безопасно от DoS; фабрики CREATE2 платят небольшую плату за слово. Тот же принцип «замерь O(n)-работу», что и в переоценке I/O из EIP-150.
04 Глубже Куда двигаться дальше