EIP-2200: цена хранилища, отложившая форк
Запись одного и того же слота хранилища трижды в одной транзакции стоила полной цены трижды — хотя на диск попадает только итоговое значение. Учёт газа по чистому эффекту это исправил, но первая попытка (EIP-1283) сделала записи настолько дешёвыми, что вызов со стипендией в 2300 газа внезапно смог менять хранилище, вновь открыв реентрантность — и это отложило Constantinople. EIP-2200 переиздаёт идею с часовым.
- состояние и хранилище (SSTORE)
- газ и стипендия при реентрантности
Стамбул, декабрь 2019 года. Каждая запись в хранилище контракта — SSTORE — стоит газа, и оценить её честно сложнее, чем кажется. Исходная схема брала плату за операцию: тронул слот — плати полную цену. Но транзакция часто пишет один и тот же слот по несколько раз, прежде чем закончиться, а на диск попадает лишь последнее значение. Брать полную плату за каждую промежуточную запись — значит завышать цену для одних из самых распространённых паттернов в сети. Решение — учёт газа по чистому эффекту — элегантно. Оно же — изменение, которое с первой попытки незаметно вновь открыло дыру для реентрантности и заставило отложить целый хардфорк за несколько часов до запуска. EIP-2200 — версия, в которой всё сделали правильно. Давайте выведем её, вместе с ошибкой.
Проблема: платите за каждую запись, но сохраняется лишь чистое изменение
Исходное ценообразование SSTORE было подоперационным: установка слота из нуля в ненулевое значение стоила 20000 газа, изменение уже ненулевого значения — 5000, а обнуление слота приносило возврат. Просто — но это игнорирует, что транзакция — это единое целое. В дерево состояния записывается только значение слота на конец транзакции; всё, что между, — черновая работа. Так что контракт, трижды увеличивающий счётчик за один вызов, платит 5000 × 3, хотя на диск когда-либо попадает только одно чистое изменение от начального значения к финальному. Тот же налог бьёт по блокировка от повторного входа Флаг в хранилище, который контракт устанавливает при входе и снимает при выходе, чтобы функцию нельзя было повторно вызвать посреди выполнения. При подоперационном ценообразовании цикл «установить, затем снять» платит полную цену в обе стороны, делая базовый паттерн безопасности дорогим. ам и по любому использованию хранилища как черновика.
→ Шаг 2: оценим чистый эффект вместо этого — но посмотрим, что это откроет.
Учёт газа по чистому эффекту и баг, отложивший Constantinople
Решение — учёт газа по чистому эффекту Оценка SSTORE через сравнение нового значения со значением слота на начало транзакции («исходным» значением), а не через фиксированную плату за каждую запись. Пустые записи и повторные записи в уже изменённый слот стоят небольшую «тёплую» плату; возвраты настроены так, что циклы вроде A→B→A почти бесплатны. : сравниваем каждую запись со значением слота на начало транзакции и берём плату соответственно. Запись, не меняющая исходное значение, или повторно затрагивающая уже изменённый слот, стоит лишь небольшую «грязную» плату — всего 200 газа, — тогда как настоящие первые изменения по-прежнему платят полную цену. Счётчики, блокировки и черновики резко дешевеют. Впервые это вошло в состав как EIP-1283, намеченный для форка Constantinople.
И вот тут всё пошло не так. В Ethereum есть соглашение по безопасности: обычный перевод эфира (.transfer() / .send()) пересылает получателю фиксированную стипендия в 2300 газа Небольшое фиксированное количество газа, пересылаемое вместе с обычным переводом эфира. Оно было намеренно выбрано так, чтобы хватало на запись события, но не хватало на изменение хранилища — чтобы fallback-функция получателя не могла провести атаку реентрантности. 200-газовые записи EIP-1283 сломали эту гарантию. , — намеренно слишком маленькую, чтобы изменить хранилище: именно это не давало fallback-функции получателя провести атаку реентрантности. Но как только SSTORE смог стоить 200 газа, fallback, работающий всего на стипендии в 2300 газа, смог всё-таки писать в хранилище. Исследователи безопасности заметили это за несколько дней до запланированной активации Constantinople. Форк был отложен, а EIP-1283 отозван.
→ Шаг 3: сохраним экономию, восстановим гарантию — с охраной.
EIP-2200: учёт по чистому эффекту за часовым
EIP-2200 заново вводит учёт газа по чистому эффекту в Istanbul с одним ключевым дополнением: часовой Охрана в начале SSTORE (SSTORE_SENTRY_GAS = 2300): если оставшегося газа 2300 или меньше, SSTORE сразу отменяется. Это восстанавливает инвариант, что стипендия в 2300 газа никогда не может изменить хранилище, закрывая дыру для реентрантности и сохраняя экономию учёта по чистому эффекту для вызовов с настоящим газом. . Прежде чем что-либо делать, SSTORE проверяет оставшийся газ: если его 2300 или меньше, операция отменяется. Fallback, работающий только на стипендии перевода, поэтому не может писать в хранилище — тот самый инвариант, который раньше бесплатно давал старый порог в 5000 газа, — а любой вызов с настоящим газом сохраняет дешёвые повторные и временные записи учёта по чистому эффекту. Экономия сохраняется; дыра для реентрантности закрывается.