Deep EVM #18: Отладка EVM-байткода — трейсы, стек-дампы и cast run
Engineering Team
Зачем нужна отладка на уровне байткода
Когда Solidity-дебаггер бессилен — когда вы работаете с Huff, inline-ассемблером или анализируете неверифицированный контракт — единственный инструмент, который остаётся, это отладка на уровне EVM-байткода. Понимание того, как читать трейсы опкодов, интерпретировать стек-дампы и воспроизводить транзакции, превращает вас из разработчика смарт-контрактов в настоящего EVM-инженера.
EVM — это стековая машина с 256-битными словами. Каждая инструкция читает из стека и/или записывает в стек. Когда транзакция завершается с revert, байткод не скажет «ошибка в строке 42» — вместо этого вы увидите последовательность опкодов, приведших к REVERT. Задача отладки — восстановить контекст.
cast run — воспроизведение транзакций
Foundry’s cast run позволяет воспроизвести любую историческую транзакцию с полным трейсом:
cast run 0xTRANSACTION_HASH --rpc-url https://eth.llamarpc.com -vvvv
Флаг -vvvv включает максимальную детализацию: каждый опкод, состояние стека, изменения памяти и хранилища.
Вывод включает:
- Дерево вызовов — иерархия CALL, DELEGATECALL, STATICCALL
- Логи — все эмитированные события
- Изменения состояния — что именно записалось в storage
- Расход газа — газ для каждого подвызова
forge debug — пошаговая отладка
forge debug запускает интерактивный дебаггер с TUI-интерфейсом:
forge debug --debug test/SimpleToken.t.sol \
--sig "test_transfer()" -vvvv
Интерфейс показывает:
- Текущий опкод и program counter
- Полное содержимое стека
- Содержимое памяти (hex + ASCII)
- Текущий calldata
Навигация: n — следующий шаг, s — шаг внутрь вызова, c — продолжить до брейкпоинта.
Чтение сырых трейсов опкодов
Рассмотрим типичный фрагмент трейса:
[PUSH1] 0x04
[CALLDATALOAD]
[PUSH1] 0x00
[MSTORE]
[PUSH1] 0x20
[PUSH1] 0x00
[SHA3]
[SLOAD]
Это классический паттерн чтения значения из mapping: загружается ключ из calldata, записывается в память, хешируется через SHA3 (KECCAK256) для получения storage-слота, затем загружается значение через SLOAD.
Понимание этих паттернов позволяет быстро ориентироваться в байткоде без исходного кода.
Декомпозиция revert-причин
Когда транзакция откатывается, EVM может вернуть данные через revert. Стандартный формат ошибки Solidity:
0x08c379a0 — Error(string)
0x4e487b71 — Panic(uint256)
Декодирование revert-данных:
cast 4byte-decode 0x08c379a0...
Для кастомных ошибок используйте cast decode-error с ABI контракта.
Практика: отладка реальной failed-транзакции
Рассмотрим пошаговый процесс:
- Получите hash транзакции — из эксплорера или логов
- Воспроизведите с трейсом:
cast run TX_HASH --rpc-url URL -vvvv - Найдите точку revert — ищите
REVERTв выводе - Проанализируйте стек перед revert — какие значения были на стеке?
- Определите паттерн — это проверка баланса? Проверка owner? Overflow?
- Проверьте гипотезу — воспроизведите с изменёнными параметрами
Инструменты для продвинутой отладки
Tenderly
Облачный дебаггер с визуальным интерфейсом. Показывает state diff, gas breakdown, и позволяет симулировать транзакции с изменёнными параметрами.
evm.codes
Интерактивная справка по опкодам EVM. Для каждого опкода показывает потребление газа, эффект на стек и примеры использования.
hevm
Символический исполнитель EVM от DappTools. Позволяет находить входные данные, приводящие к определённому состоянию — мощный инструмент для поиска уязвимостей.
Отладка storage layout
Понимание раскладки хранилища критично для отладки:
# Прочитать слот хранилища
cast storage CONTRACT_ADDR 0 --rpc-url URL
# Для mapping: вычислить слот
cast keccak \
$(cast abi-encode "f(address,uint256)" ADDR SLOT)
Для proxy-контрактов помните об EIP-1967 слотах:
- Implementation:
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc - Admin:
0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
Заключение
Отладка EVM-байткода — это навык, который отличает продвинутого блокчейн-разработчика. Инструменты Foundry — cast run и forge debug — делают этот процесс доступным. Регулярная практика чтения трейсов, понимание паттернов опкодов и знание storage layout позволят вам быстро диагностировать любые проблемы на уровне EVM.