ГЛАВНАЯ / ИНСТРУКЦИИ / CHECKS-EFFECTS-INTERACTIONS: ПОРЯДОК ОПЕРАЦИЙ ПРОТИВ REENTRANCY
Checks-Effects-Interactions: порядок операций против reentrancy
Checks-Effects-Interactions (CEI) – способ упорядочить функцию: сначала проверить условия, затем зафиксировать изменения собственного состояния и только после этого обращаться к внешнему коду. Такой порядок уменьшает окно, в котором callback может использовать старые данные, но его нужно применять к целому инварианту и корректно обрабатывать отказ взаимодействия.
Проверки, эффекты и взаимодействия имеют разный смысл
Checks проверяют право вызвать функцию, корректность входных данных, статус протокола и достаточность баланса. Effects обновляют storage так, чтобы собственный учёт уже отражал результат операции. Interactions вызывают другой контракт или передают управление внешнему адресу.
В функции снятия средств безопасный порядок обычно означает: определить доступную долю, убедиться в условиях, уменьшить учётную долю и затем отправить актив. Если сначала отправить Ether, получатель-контракт может выполнить callback до обновления баланса. Пример описывает общий принцип, а не универсальную реализацию для любого токена или протокола.
- checks определяют допустимость операции
- effects приводят storage к согласованному состоянию
- interactions передают управление после обновлений
| Этап | Вопрос | Пример |
|---|---|---|
| Checks | Можно ли выполнить действие? | Доступ, сумма, статус и лимит |
| Effects | Как меняется внутреннее состояние? | Обновить долю до передачи управления |
| Interactions | Куда передаётся управление? | Внешний вызов или перевод актива |
Согласованное состояние нужно записать до callback
Перечислите все effects, которые должны считаться завершёнными до вызова наружу. Иногда это больше одной переменной: balance, reserved amount, nonce, статус заявки и aggregate total могут образовывать единый инвариант. Обновление только одного поля способно оставить связанную функцию в противоречивом состоянии.
Если внешний вызов возвращает ошибку, контракт должен явно определить результат. При bubbling revert внешняя транзакция откатывает изменения её call tree; при обработке false без повторного revert код может оставить обновлённое состояние, хотя передача актива не удалась. Оба поведения требуют осознанного соответствия логике протокола.
- обновления охватывают весь инвариант
- ошибка передачи обработана явно
- неуспешное взаимодействие не выдаётся за успешное
Проверяйте не только прямую передачу Ether
Внешнее взаимодействие может происходить при вызове произвольного контракта, token hook, отправке Ether или маршрутизации через router. Интерфейс функции не всегда сразу показывает, что реализация адреса может выполнить callback. Даже проверенный контракт может обратиться к другому адресу и вернуть управление через цепочку вызовов.
Составьте список всех внешних переходов управления и определите, какое состояние уже должно быть согласовано к каждому из них. Если одна операция содержит несколько взаимодействий, повторите анализ после каждого вызова: следующий адрес может увидеть уже изменённые данные и изменить дальнейший путь исполнения.
CEI не заменяет проверку всей модели состояния
CEI снижает риск reentrancy, но не доказывает корректность доступа, арифметики или экономической модели. Сложный протокол может требовать reentrancy guard, pull-payment схемы, отдельного статуса обработки или иной архитектуры. Если guard защищает только одну функцию, связанные entrypoint всё ещё нужно проверить вместе.
Перед применением паттерна моделируйте callback и не полагайтесь на предположение, что внешний контракт не будет выполнять дополнительный код. Проверяйте все ветви, включая отказ адресата, повторный вызов, вложенные взаимодействия и пограничные суммы. Автоматические анализаторы помогают находить подозрительные пути, но не доказывают, что инвариант выбран правильно.
Подтвердите порядок тестами на внешнем адресате
Создайте тестовый контракт-получатель, который в callback пытается вызвать исходный entrypoint и связанные функции. Проверьте, что он видит уже обновлённое состояние либо получает отказ без нарушения инварианта. Добавьте тест неуспешного внешнего вызова и убедитесь, что состояние и перевод согласованы.
В ревью отметьте места изменения storage и взаимодействия, а затем сопоставьте их с трассой теста. Проверяйте эффект транзакции целиком: событие об успешном выводе не должно появляться до подтверждённого шага, а остаток баланса должен соответствовать правилам контракта.
Частые вопросы
В каком порядке расположены CEI этапы?
Checks, затем Effects, после них Interactions.
CEI гарантирует отсутствие всех атак?
Нет. Паттерн закрывает часть рисков порядка операций, но не заменяет аудит модели и доступа.
Если внешний вызов неуспешен, state всегда откатывается?
Только если ошибка откатывается вызывающим кодом. При обработке false без revert изменения могут сохраниться.
Нужно ли обновлять все переменные до callback?
Нужно привести к согласованному состоянию весь инвариант, который может увидеть внешний код.
Нужен ли reentrancy guard при CEI?
Зависит от пути вызовов и модели состояния; одно не исключает необходимости другого.
Чек-лист: checks-effects-interactions: порядок операций против reentrancy
- предусловия отделены от effects;
- все связанные storage-поля найдены;
- внешние взаимодействия перечислены;
- инвариант согласован до callback;
- ошибка взаимодействия обработана явно;
- повторный вход проверен для связанных функций;
- успешный результат подтверждается корректным состоянием и тестом.
Материал подготовлен редакцией сайта и проверен 2 ноября 2026 года. Условия бирж, кошельков и блокчейн-сетей могут меняться. Перед операцией сверяйте данные в интерфейсе выбранного сервиса.