Перейти к содержанию

Виртуальная машина Ethereum (EVM)

Учитывающее форки руководство по транзакциям EVM, кадрам вызовов сообщений, байт-коду, стеку, памяти, хранилищу, газу, REVERT, DELEGATECALL, прекомпилированным контрактам, квитанциям и сверке состояния.

Обновлено

Только в образовательных целях; не является инвестиционным советом или инвестиционной рекомендацией. Инвестиции могут привести к убыткам.

Краткий ответ

Виртуальная машина Ethereum — это машина перехода состояния уровня исполнения, которая интерпретирует байт-код по правилам конкретного форка. При одинаковом допустимом предсостоянии, одинаковой транзакции или сообщении верхнего уровня, окружении блока и спецификации форка совместимые клиенты исполнения должны вычислить одно и то же постсостояние или результат сбоя. EVM не выбирает порядок транзакций, не обеспечивает финальность консенсуса, не удостоверяет интерфейс и не делает каждую EVM-совместимую цепь столь же безопасной, как Ethereum.

Подписанная извне транзакция является протокольным объектом верхнего уровня. Взаимодействия контрактов состоят из вложенных вызовов сообщений и кадров вызова, а не из независимых транзакций с собственными nonce, квитанцией и хешем. В каждом кадре есть код, счетчик команд, стек из 256-битных слов, память, calldata, returndata, газ и контекст исполнения; постоянное хранилище принадлежит аккаунту, а время жизни временного хранилища ограничено транзакцией по правилам форка.

Как это работает

  1. Зафиксируйте снимок исполнения: цепь и chainId, сеть, номер и хеш блока, канонический или финализированный статус, форк, клиент исполнения и редакцию спецификации, предсостояние или корень состояния, тип транзакции, исходные подписанные байты, хеш и квитанцию транзакции. Детерминированность обусловлена именно этим контекстом.
  2. Декодируйте конверт транзакции и отделите допустимость до исполнения от самого исполнения. Проверьте подпись и отправителя, nonce, адрес назначения или создание, value, calldata, лимит газа, поля комиссии, список доступа и поля конкретного типа. Отклоненная до включения транзакция — не то же самое, что включенная транзакция, которая исполнилась и откатилась.
  3. Постройте сообщение верхнего уровня и полное дерево вызовов. Запишите кадры CALL, STATICCALL, DELEGATECALL, создания контрактов и прекомпилированных контрактов; вызывающего, адрес контекста, адрес кода, msg.sender, msg.value, передачу value, calldata, returndata, переданный газ и флаг успеха. Метка обозревателя «внутренняя транзакция» — представление трассировки, а не подписанная транзакция.
  4. Отслеживайте в каждом кадре счетчик команд, стек, память, calldata, returndata, журналы, постоянное хранилище и журнал временного хранилища. CALL использует адрес и контекст хранилища вызываемого контракта; DELEGATECALL исполняет целевой код в адресе и хранилище вызывающего, сохраняя исходные sender и value; STATICCALL запрещает изменение состояния.
  5. Примените правила газа целевого форка: intrinsic gas, динамические стоимости опкодов, расширение памяти, холодные и теплые обращения, передачу газа вызову, stipend, стоимость прекомпилированных контрактов, возвраты и их пределы. Затем отдельно от value вычислите комиссию по использованному газу и эффективной цене. Оценка газа условна и не является гарантией.
  6. Определите исходы кадров с учетом области действия. RETURN фиксирует кадр, если затем фиксируются его предки. REVERT откатывает кадр и потомков, возвращает данные и не обязан израсходовать весь оставшийся газ кадра; исключительные остановки отличаются. Родитель может обработать неудачный низкоуровневый вызов и продолжить, поэтому статус верхней квитанции может быть 1, даже если дочерний вызов завершился неудачно. Включенный сбой верхнего уровня все равно расходует nonce отправителя и оплаченный газ.
  7. Сверьте статус квитанции, использованный газ, журналы, созданный адрес и возвращенные данные с балансами, nonce, кодом, постоянным и временным хранилищем, реестрами токенов, трассировками и корнем состояния блока до и после исполнения. При необходимости повторите исполнение независимым клиентом; отдельно от финальности консенсуса проверяйте реализации proxy, схемы хранилища, прекомпилированные контракты, целевую EVM компилятора и обновления форков.

Разобранные примеры

  • Включенный откат верхнего уровня и учет комиссии. У транзакции типа 2 лимит газа 80,000, использовано 52,000, базовая комиссия 20 gwei, максимальная приоритетная комиссия 3 gwei, максимальная комиссия 40 gwei. Эффективная цена равна min(40, 20 + 3) = 23 gwei; фактическая комиссия — 52,000 * 23 gwei = 0.001196 ETH, из которых 52,000 * 20 gwei = 0.001040 ETH сжигается, а 52,000 * 3 gwei = 0.000156 ETH составляет приоритетную комиссию. Неиспользованные 28,000 gas не оплачиваются. При откате верхнего исполнения изменения хранилища, передача value и журналы откатываются, но nonce и фактическая комиссия остаются.
  • Обработанный сбой дочернего вызова. Изначально в контракте A A.x = 5. Он вызывает B; B записывает B.y = 9, создает журнал и выполняет REVERT. Запись и журнал B откатываются. A получает success = false, записывает A.x = 7 и нормально завершается. Итоговый статус квитанции — 1, значение A.x = 7, а B сохраняет прежнее значение. Успех верхнего уровня не доказывает успех каждого дочернего вызова.
  • Контекст хранилища DELEGATECALL. У proxy slot0 = 5, а у аккаунта реализации slot0 = 99. Код реализации читает слот 0, прибавляет 7 и сохраняет результат. При исполнении через DELEGATECALL proxy меняется на slot0 = 12, а реализация остается slot0 = 99: применяются адрес и хранилище proxy, сохраняются исходные sender и value. Несовместимая схема хранилища может повредить состояние proxy.
  • SELFDESTRUCT в рамках форка. По правилу EIP-6780 существующий контракт с 2 ETH выполняет SELFDESTRUCT в пользу B. B получает 2 ETH, баланс контракта обнуляется, но существующий аккаунт, код и хранилище не удаляются. Удаление сохраняется лишь для контракта, созданного и уничтоженного в одной транзакции. Это правило конкретного форка, его нельзя переносить назад или на любую EVM-цепь.

Риски

  • Повторное исполнение для неверной цепи, блока, форка, клиента или предсостояния.
  • Принятие неканонического или реорганизованного состояния за окончательный контекст.
  • Смешение отказа предварительной проверки с откатом включенного исполнения.
  • Предположение, что pending-симуляция совпадет с последующим включением.
  • Игнорирование верхнего отката, исключительной остановки или нехватки газа.
  • Пропуск дочернего сбоя, обработанного родителем.
  • Неверное понимание контекста, адреса кода, вызывающего, sender или value.
  • Повреждение proxy из-за несовместимой схемы хранилища при DELEGATECALL.
  • Допущение повторного входа или небезопасной передачи value и управления.
  • Доверие непроверенным returndata, флагам успеха или пользовательским ошибкам.
  • Принятие журналов или трассировок за окончательное состояние.
  • Ошибка в холодных и теплых обращениях, памяти или переданном вызову газе.
  • Ошибочное применение возвратов, их пределов, stipend или правила 63/64.
  • Неверный адрес, вход, цена газа или семантика форка прекомпилированного контракта.
  • Смешение времени жизни постоянного хранилища, памяти и временного хранилища.
  • Ожидание изменения состояния внутри STATICCALL.
  • Применение предположений об удалении SELFDESTRUCT до EIP-6780.
  • Пропуск изменения реализации proxy, администратора или цели компилятора.
  • Расхождение или отсутствие обновления правил клиента исполнения.
  • Вывод о безопасности консенсуса, моста, токена, управления или финальности из совместимости с EVM.

Распространенные заблуждения

  • Исходный код Solidity непосредственно исполняется в сети.
  • Откатившаяся включенная транзакция ничего не стоит и не меняет nonce.
  • Статус квитанции 1 доказывает ожидаемый успех всех внутренних вызовов.
  • Событие или трассировка являются окончательным состоянием активов и хранилища.
  • Совместимость с EVM гарантирует одинаковые опкоды, газ, прекомпилированные контракты, консенсус и безопасность.

Связанные темы

Источники

Навигация

Поиск по вики...