ABI контракта Ethereum: как читать интерфейс и понимать его ограничения

ABI контракта Ethereum — это описание интерфейса, которое помогает приложениям кодировать вызовы функций и интерпретировать возвращаемые данные. Аббревиатура ABI означает Application Binary Interface — двоичный интерфейс приложения. В JSON-файле указаны, в частности, имена функций, типы параметров и типы возвращаемых значений. Такой файл описывает интерфейс; для оценки реализации нужны дополнительные сведения.

Полезно разделять три вопроса: что объявлено в интерфейсе, как представлены данные и что делает исполняемая программа. Это позволяет читать ABI осмысленно, не принимая знакомое название функции или понятный результат декодирования за доказательство безопасности.

Чем ABI отличается от исходного кода и байткода

В документации Ethereum о компиляции JSON ABI описан как файл со сведениями о развёрнутом контракте и его функциях. Там же объясняется, что для выполнения контракта виртуальной машине Ethereum, или EVM, нужен байткод.

Исходный код появляется в этой цепочке до исполнения. В примере Multiply7 из документации Ethereum JSON-RPC контракт на Solidity компилируется в байткод для EVM. Пример показывает связь между исходным кодом и результатом компиляции. Сам по себе он не подтверждает соответствие исходников какому-либо другому развёрнутому контракту.

Из этих документированных ролей следует разграничение трёх представлений:

  • Исходный код — программа, переданная на компиляцию в рассматриваемом примере.
  • Байткод — представление программы, которое выполняет EVM.
  • JSON ABI — описание интерфейса для интерпретации функций и связанных с ними типизированных данных.

Это логический вывод из назначения каждого представления. Наличие одного из них само по себе не устанавливает его соответствие двум другим. В частности, найденный ABI-файл ещё не подтверждает, что он относится именно к обсуждаемому развёрнутому контракту.

Поэтому при чтении интерфейса стоит отдельно фиксировать, что перед вами за файл и на каком основании его связывают с конкретным контрактом. Понятная структура JSON помогает изучать описание, но вопрос соответствия исполняемому коду требует собственных подтверждений.

Что можно узнать из записи о функции

Спецификация ABI Solidity определяет поля JSON-описания интерфейса. Для именованной функции удобно начать с трёх элементов:

  • name — имя функции.
  • inputs — упорядоченное описание входных параметров с именем и каноническим типом каждого из них.
  • outputs — описание возвращаемых параметров с помощью объектов, устроенных аналогично элементам inputs.

Для параметров-кортежей также используется поле components, описывающее их составные части. Массив outputs может быть пустым. Эти детали важны: описание интерфейса должно сохранять структуру данных, а не просто перечислять узнаваемые названия.

Не каждая запись ABI устроена как именованная функция. У записей конструктора, receive и fallback нет имени и выходных параметров; у receive и fallback также нет входных параметров. Поэтому сначала нужно определить вид записи, а затем читать соответствующие ему поля.

Объявленные выходные параметры показывают ожидаемую структуру возвращаемых данных. Из такого объявления ещё не следует, что функция уже исполнялась или вернула конкретное значение. Описание результата и свидетельство полученного результата — разные сведения.

Как имя и типы связаны с селектором функции

Для обычного вызова функции по правилам ABI селектор получают из её канонической сигнатуры. Сигнатура состоит из имени функции и заключённого в круглые скобки списка типов параметров. Селектор — первые четыре байта хеша Keccak-256 этой сигнатуры.

Следовательно, узнавания имени недостаточно для полного понимания интерфейса: канонические типы входных параметров тоже входят в сигнатуру. Если при сравнении проверить только название, часть сведений, участвующих в получении селектора, останется без внимания. Практически это означает, что читать строку функции нужно вместе с её входными типами и их порядком.

У типов возвращаемых значений другая роль. Они не входят в сигнатуру, из которой получают селектор, хотя полное JSON-описание содержит выходные параметры. Отсюда следует ограниченный вывод: один селектор не предоставляет схему возвращаемых данных. Основание этого вывода — различие между правилами получения селектора и описанием выходных параметров.

Эта связь объясняет устройство интерфейса. Она не устанавливает безопасность реализации, связанной с селектором, и сама по себе не служит заключением о надёжности контракта.

Почему для декодирования нужна схема

Кодирование ABI зависит от типов кодируемых значений. Спецификация прямо указывает, что формат не является самоописываемым: для декодирования нужна схема. Здесь схема означает сведения о структуре и типах, позволяющие интерпретировать данные.

Поэтому возникают две отдельные задачи. Сначала нужно определить объявленные типы и структуру. Затем — установить, почему это описание подходит к рассматриваемым данным. Наличие ABI помогает решить первую задачу в той мере, в какой файл содержит нужное описание. Его соответствие конкретному развёрнутому контракту остаётся отдельным вопросом.

Возвращаемые функцией значения объединяются в кортеж и кодируются вместе по правилам ABI. Поэтому описание outputs необходимо учитывать при интерпретации ответа, хотя возвращаемые типы не участвуют в получении селектора.

Понятный результат декодирования следует оценивать с оговоркой о пригодности выбранной схемы. Логический предел здесь такой: поскольку интерпретация зависит от предоставленного описания, чтение байтов с его помощью само по себе не подтверждает свойства исполняемой реализации. Структурированное представление данных ещё не отвечает на вопрос, насколько обоснован выбор именно этой схемы.

Гипотетический пример чтения интерфейса

Представим вымышленный учебный контракт с функцией lookupRecord. В его гипотетическом ABI объявлен один входной параметр recordId типа uint256 и два выходных параметра: found типа bool и label типа string. Это придуманный пример для изучения интерфейса, а не описание развёрнутого контракта или предложение взаимодействовать с ним.

По такому описанию учащийся может определить имя функции, тип входного параметра и структуру ответа из двух частей. Каноническая сигнатура будет выглядеть как lookupRecord(uint256). Объявленные выходные типы в сигнатуру для получения селектора не войдут.

Теперь предположим, что в задании дан только этот интерфейс и поставлен вопрос: какую подпись вернёт функция для записи 7? Обоснованный ответ — результат не установлен. Объявление задаёт схему ответа, но в условии нет ни сведений о поведении реализации, ни результата исполнения.

Также из одного названия не следует, что функция ищет данные в определённой базе, применяет конкретное правило или возвращает достоверную информацию. Это вывод об ограниченности сведений в учебном примере. Для таких заключений потребовались бы дополнительные данные о реализации и её результате.

Пример показывает полезную границу чтения ABI: можно уверенно описать объявленный интерфейс и одновременно оставить неизвестным фактическое поведение программы. Неопределённость результата здесь не означает, что контракт обязательно работает неправильно.

Проверочный список для чтения ABI

  1. Определите вид материала. Различайте JSON-описание интерфейса, исходный код и исполняемый байткод.
  2. Проверьте основание связи. Уточните, какие сведения связывают предоставленный ABI с обсуждаемым развёрнутым контрактом.
  3. Определите вид записи. Применяйте набор полей именованной функции только к подходящим записям.
  4. Сохраните структуру входных параметров. Учитывайте имена, канонические типы, порядок и компоненты кортежей, если они есть.
  5. Читайте выходные параметры отдельно. Отличайте объявленную структуру ответа от наблюдаемого результата.
  6. Точно понимайте сигнатуру селектора. В ней участвуют имя и канонические входные типы, но отсутствуют возвращаемые типы.
  7. Обозначьте предпосылки декодирования. Укажите, на какую схему опирается интерпретация данных.
  8. Ограничьте заключение. Чтение интерфейса само по себе не доказывает безопасность реализации.

Последнее ограничение — логический вывод из описательной роли ABI, зависимости декодирования от схемы и исполняемой роли байткода. Это не заключение о небезопасности какого-либо конкретного контракта. Ссылки на документацию подтверждают основу объяснения, но не удостоверяют надёжность отдельной реализации.

Если нужно перейти от описания интерфейса к смыслу различных подтверждений операции, полезен материал о том, как проверяется транзакция Ethereum и что подтверждает каждый этап. Он помогает отделить вопросы исполнения от других этапов проверки; здесь рассмотрено именно чтение интерфейса.

Образовательный материал подготовлен с помощью ИИ. Гипотетический пример предназначен для понимания интерфейсов и не является рекомендацией отправлять транзакцию или подписывать запрос кошелька.