Что такое индексация смарт-контрактов в Web3 разработке? (Начальные знания не требуются)

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Всем привет, решил перевести свою англоязычную статью, в которой я скомпилировал знания полученные в течение года работы в web3 инфраструктурном провайдере о данных на EVM блокчейнах и инструментах разработчика для доступа к ним.

Сложно сказать, что культура инженерии данных глубоко укоренилась в сообществе разработчиков Web3. И не каждый разработчик может легко определить, что означает индексация в контексте Web3. Я хотел бы уточнить некоторые детали на эту тему и поговорить об инструменте под названием The Graph, который стал де-факто стандартом индустрии для доступа к данным на блокчейне для создателей DApp'ов (децентрализованных приложений).

Картинка, на которой я пытался изобразить пропасть между двумя мирами разработчиков, но никто не понял моей задумки :)
Картинка, на которой я пытался изобразить пропасть между двумя мирами разработчиков, но никто не понял моей задумки :)

Начнем с индексации

Индексация в базах данных - это процесс создания структуры данных, которая сортирует и организует данные в базе данных таким образом, чтобы запросы на поиск могли выполняться эффективно. Создав индекс в таблице базы данных, сервер базы данных может быстрее искать и извлекать данные, соответствующие критериям, указанным в запросе. Это помогает улучшить производительность базы данных и сократить время, необходимое для извлечения информации.

Но что насчет индексации в блокчейнах? Самая популярная архитектура блокчейна - это EVM (Ethereum Virtual Machine).

Ethereum Virtual Machine (EVM) - это среда выполнения, которая выполняет смарт-контракты на блокчейне Ethereum. Это компьютерная программа, которая работает на каждом узле сети Ethereum. Она отвечает за выполнение кода смарт-контрактов и также предоставляет функции безопасности, такие как песочница и контроль использования газа. EVM гарантирует, что все участники сети Ethereum могут выполнять смарт-контракты последовательно и безопасно.

Как вы, возможно, знаете, данные на блокчейне хранятся в виде блоков с транзакциями внутри. Также вы, возможно, знаете, что существует два типа учетных записей:

Внешний собственный аккаунт (Externally owned account) - описывается любым обычным адресом кошелька
Аккаунт контракта (Contract account) - описывается любым адресом развернутого смарт-контракта

Если вы отправляете некоторое количество эфира со своего аккаунта на любой другой аккаунт внешнего владельца - ничего дополнительного за рамками такой транзакции не происходит.

Но вот если вы отправите некоторое количество эфиров на адрес смарт-контракта с любой полезным нагрузкой, вы на самом деле запустите какой-то метод в смарт-контракте, который технически создает так называемые “internal” транзакции.

Но если любая транзакция может быть найдена в блокчейне, почему бы не преобразовать все данные в большую постоянно обновляющуюся базу данных, которую можно будет запрашивать в формате, похожем на SQL?

Проблема в том, что вы можете получить доступ к данным смарт-контракта только если у вас есть "ключ" для расшифровки его. Без этого "ключа" данные смарт-контрактов в блокчейне на самом деле представляют собой байткод. Этот ключ называется ABI (Application Binary Interface).

ABI (Application Binary Interface) - это стандарт, который определяет, как смарт-контракт взаимодействует с внешним миром, включая другие смарт-контракты и пользовательские интерфейсы. Он определяет структуру данных, сигнатуры функций и типы аргументов смарт-контракта для обеспечения правильного и эффективного общения между контрактом и его пользователями.

У любого смарт-контракта в блокчейне есть ABI. Проблема в том, что у вас может не быть ABI для смарт-контракта, который вас интересует. Иногда вы можете найти файл ABI (который на самом деле является JSON-файлом с названиями функций и переменных смарт-контракта, похожим на интерфейс для общения с ним)

  • на Etherscan (если смарт-контракт был верифицирован, то есть байт код сравнили с тем, что получается из исходников)

  • на GitHub (если разработчики открыли исходный код проекта)

  • или если смарт-контракт относится к любому стандартному типу, такому как ERC-20 (взаимозаеменяемые токены как USDT), ERC-721 (невзаимозаменяемые токены или NFT) и т. д.

Конечно, если вы разработчик смарт-контракта, у вас есть ABI, потому что этот файлик генерируется во время компиляции.

Как это выглядит с точки зрения разработчика

Но давайте не будем останавливаться на взгляде на данные через ABI. Что если мы рассмотрим эту тему с точки зрения самого разработчика смарт-контракта? Что такое смарт-контракт? Ответ как бы намного проще, чем вы бы могли подумать. Вот простое объяснение для тех, кто знаком с объектно-ориентированным программированием:

Смарт-контракт в коде разработчика - это класс с некоторыми полями и методами (для смарт-контрактов EVM-совместимых цепочек обычно используется язык программирования Solidity). И задеплоенный смарт-контракт становится как бы уже объектом этого класса. Таким образом, он живет своей жизнью, позволяя пользователям вызывать его методы и изменять его внутренние поля.

Обратите внимание, что любой вызов метода с изменением состояния смарт-контракта означает транзакцию, которая обычно сопровождается событием ("ивентом"), которое разработчик "эмитирует" прямо из кода. Проиллюстрируем вызов функции смарт-контракта ERC-721 (обычный стандарт для коллекций невзаимозаменяемых токенов вроде BoredApeYachtClub), который излучает событие при переходе права собственности на NFT.

/**
     * @dev Переводит tokenId от отправителя from к получателю to.
     * В отличие от {transferFrom}, это не накладывает ограничений на msg.sender.
     *
     * Требования:
     *
     * - to не может быть адресом zero.
     * - tokenId token должен принадлежать отправителю from.
     *
     * Создает событие {Transfer}.
     */

    function _transfer(address from, address to, uint256 tokenId) internal virtual {
        address owner = ownerOf(tokenId);
        if (owner != from) {
            revert ERC721IncorrectOwner(from, tokenId, owner);
        }
        if (to == address(0)) {
            revert ERC721InvalidReceiver(address(0));
        }

        _beforeTokenTransfer(from, to, tokenId, 1);

        // Проверяем, что tokenId не был передан через хук _beforeTokenTransfer
        owner = ownerOf(tokenId);
        if (owner != from) {
            revert ERC721IncorrectOwner(from, tokenId, owner);
        }

        // Очищаем одобрения предыдущего владельца
        delete _tokenApprovals[tokenId];

        // Уменьшаем баланс с проверенной арифметикой, потому что переопределение ownerOf может
        // нарушить предположение, что _balances[from] >= 1.
        _balances[from] -= 1;

        unchecked {
            // _balances[to] может переполниться в условиях, описанных в _mint. Для этого потребуется
            // сделать минтинг всех 2**256 токенов, что на практике невозможно.
            _balances[to] += 1;
        }

        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);

        _afterTokenTransfer(from, to, tokenId, 1);
    }

Итак, что мы можем здесь заметить. Чтобы передать NFT с вашего адреса на любой другой адрес, вам нужно вызвать функцию _transfer, передав значения этих двух адресов и ID этого NFT. В коде вы можете видеть, что будут выполнены некоторые проверки, а затем изменятся балансы пользователей. Но важно то, что в конце кода функции есть строка:

emit Transfer(from, to, tokenId);

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

Теперь мы определили все необходимые понятия для объяснения того, что такое индексация.

Учитывая тот факт, что любой смарт-контракт (будучи объектом некоторого класса) живет своей жизнью, постоянно будучи вызываемым пользователями (и другими смарт-контрактами) и меняя состояние (в то же время транслируя "ивенты"), индексацию можно определить как процесс сбора данных смарт-контракта (любые внутренние переменные внутри контракта и не только те, которые явно передаются) на протяжении его жизненного цикла, сохраняя эти данные вместе с идентификаторами транзакций (хэш) и номерами блоков, чтобы иметь возможность найти любые детали в будущем.

Это очень важно отметить, потому что используя обычный интерфейс блокчейн ноды, просто невозможно получить, например, первую транзакцию кошелька «А» с токеном «B» или самую большую транзакцию в смарт-контракте «C» (или любую другую информацию), если смарт-контракт явно не хранит эти данные (как мы знаем, это очень дорого).

Вот зачем нам нужна индексация. Простые вещи, которые мы можем делать в базе данных SQL, становятся невозможными в блокчейне. Без индексации.

Другими словами, «индексация» здесь - это синоним сбора данных смарт-контракта, потому что отсутствие индексации означает отсутствие доступа к данным в Web3.

Как же разработчики раньше индексировали данные? Они делали это с нуля, для этого:

- Они пишут высокопроизводительный код на быстрых языках программирования, таких как Go, Rust и т.д.

- Они настраивают базу данных для хранения данных.

- Они настраивают API для доступа к данным из приложения.

- Они запускают архивную ноду блокчейна.

- На первом этапе они сканируют весь блокчейн, находя все транзакции, связанные с определенным смарт-контрактом.

- Они обрабатывают эти транзакции, сохраняя новые объекты и обновляя существующие объекты в базе данных.

- Когда они достигают текущего блока (chain head), им нужно переключиться на более сложный режим для обработки новых транзакций, потому что каждый новый блок (даже цепочка блоков) может быть отклонен из-за реорганизации цепи (что повлечет некорректность в данных в базе).

- Если цепь была реорганизована, им нужно вернуться к блоку, когда сеть реорганизовалась и пересчитать все до нового chain head.

Как вы можете заметить, это не очень просто как разработать, так и поддерживать в реальном времени, потому что каждый сбой на уровне ноды может потребовать еще дополнительных шагов для восстановления актуальности данных. Вот собственно почему появился The Graph. Это простая идея о том, что разработчикам и конечным пользователям нужен доступ к данным смарт-контракта без такиого рода проблем.

Проект The Graph определил парадигму под названием "сабграф" (subgraph), согласно которой для извлечения данных из смарт контракта вам нужно описать 3 вещи:

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

2. Как хранить данные. Какие таблицы следует создать в базе данных для хранения данных смарт контракта? Ответ будет найден в файле "схемы".

3. Как собирать данные. Какие переменные следует сохранять из "ивентов", какие сопутствующие данные (например, хеш транзакции, номер блока, результат вызовов других методов и т. д.) следует также собирать и как они должны быть помещены в определенные схемы. Это описывается в третьем файле.

Эти три вещи можно красиво определить в следующих трех файлах:

  • subgraph.yaml — файл манифеста

  • schema.graphql — описание схемы

  • mapping.ts — файл AssemblyScript

Благодаря этому стандарту крайне легко описать весь процесс индексации, следуя любому из этих обучающих примеров (они пока на английском и я планирую их перевести, если будет заметный интерес к текущей статье, так что оставьте апвоут или коммент под статьей, чтобы я понял, что стоит это продолжать):

  • Как получить доступ к данным смарт-контракта Tornado Cash с использованием сабграфов The Graph

  • Как получить доступ к транзакциям токена PEPE с использованием сабграфов The Graph и запросов к ChatGPT

  • Руководство для начинающих по работе с The Graph

  • Объяснение схем сабграфов

  • Как получить доступ к данным смарт контракта в реальном времени из кода на Python

Еще больше мануалов на тему можно найти здесь.

И вот как это выглядит:

Как вы видите здесь, The Graph целиком отвечает за индексирование. Однако вам все еще нужно запускать graph-node (это ПО с открытым исходным кодом от The Graph), а это на деле оказывается задача посложнее, чем просто запускать ноду (которая так и норовит все время выйти из актуального состояния). И здесь происходит очередной сдвиг парадигмы.

Разработчики в прошлом запускали собственные ноды блокчейна, прекратив это делать постепенно и передав эту заботу Web3 инфраструктурным пройдерам. The Graph предложил другое архитектурное упрощение: платформа для хостинга сабграфов The Graph, которая работает для разработчика ("user" здесь) таким образом:

В этом случае пользователь (или разработчик) не должен запускать собственный индексер или граф-ноду, но по-прежнему может контролировать все алгоритмы и даже не попадать в vendor lock, потому что разные провайдеры используют одинаковый формат описания The Graph (Chainstack в частности, полностью совместим с The Graph, но также стоит проверить это утверждение для вашего Web3 провайдера инфраструктуры, если таковой имеется). И это имеет большое значение, потому что это помогает разработчикам ускорить процесс разработки и снизить операционные издержки на обслуживание.

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

Что я пропустил в предыдущем повествовании.

  • Как вы могли заметить, The Graph использует GraphQL вместо REST API. Это позволяет пользователям гибко составлять запросы к любым созданным таблицам, объединяя их и легко фильтруя. Есть хорошее видео о том, как его освоить. Кроме того, ChatGPT может помочь писать запросы GraphQL, как я показал в этом туториале.

  • У The Graph есть собственный сервис для хостинга сабграфов с большим количеством готовых к использованию сабграфов. Он бесплатен, но, к сожалению, не соответствует требованиям продакшена (надежность, SLA, поддержка), так как фактически является песочницей для децентализованной сети, кроме того, синхронизация происходит медленнее, чем с платными решениями, но все равно может быть использована для разработки. Обучающий материал о том, как использовать готовые к использованию субграфы с Python, можно найти здесь.

  • Если вы собираетесь использовать субграфы в производстве, я бы порекомендовал обратиться к опытному поставщику инфраструктуры Web3, такому как Chainstack, чтобы достичь эффективности с точки зрения затрат вместе с надежностью и скоростью.

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

    В общем, если вы дочитали до сюда, и вам хотелось бы, чтобы я перевел и другие материалы по доступу к данным на блокчейне или разработке смарт-контрактов, поставьте апвоут и/или напишите в комментариях, про что еще было бы интересно почитать.

Источник: https://habr.com/ru/articles/741536/


Интересные статьи

Интересные статьи

Одна из основных задач системного аналитика это формализация и описание тех или иных понятий. И вот уже проработав системным аналитиком в течении 5-ти лет и встречаясь со словом система десятки раз за...
Добрый день, читатели Хабра, представляю перевод статьи по разработке dApp. Приятного чтения.Мир онлайн-технологий быстро переходит к веб 3.0 (перевод этой статьи можете найти здесь). Похоже, что люди...
Для анализа поведения пользователей и улучшения качества обслуживания клиентов компании используют продуктовую аналитику. Аналитика упрощает отслеживание пользователей, поскольку сбор и управление дан...
На днях в журнале Nature вышла примечательная статья, в которой группа специалистов Стэнфордского университета поделилась необычными находками по теме внетелесных переживаний и диссо...
Исторически утилиты командной строки в Unix-системах развиты лучше чем в Windows, однако с появлением нового решения ситуация изменилась. Читать дальше →