Архитектура приложений Web 3.0 (или «DApps») полностью отличается от приложений Web 2.0.
Возьмем, к примеру, Medium, простой блог-сайт, который позволяет пользователям публиковать свой собственный контент и взаимодействовать с контентом других.
Как приложение Web 2.0 это может показаться простым, но в архитектуру Medium входит многое, чтобы сделать все это возможным:
Во-первых, должно быть место для хранения важных данных, таких как информация о пользователях, сообщения, теги, комментарии, лайки, и так далее. Для этого требуется постоянно обновляемая база данных.
Во-вторых, бэкэнд (написанный на таком языке, как Node.js, Java или Python) должен определять бизнес-логику Medium. Например, что происходит, когда новый пользователь регистрируется, публикует новый блог или комментирует чей-то еще блог?
В-третьих, фронтэнд (обычно написанный на JavaScript, HTML и CSS) должен определять логику пользовательского интерфейса Medium. Например, как выглядит сайт и что происходит, когда пользователь взаимодействует с каждым элементом на странице?
Собрав все это вместе, когда вы пишете сообщение в блоге на Medium, вы взаимодействуете с его фронтэндом, который общается с его бэкэндом, который общается с его базой данных. Весь этот код размещается на централизованных серверах и отправляется пользователям через интернет-браузер. Это хороший общий обзор того, как сегодня работает большинство приложений Web 2.0.
Но все это меняется.
Технология блокчейн открыла новое захватывающее направление для приложений Web 3.0. В этой статье мы сосредоточимся на том, что предлагает блокчейн Ethereum.
Что отличает Web 3.0?
В отличие от приложений Web 2.0, таких как Medium, Web 3.0 устраняет посредников. Нет централизованной базы данных, в которой хранится состояние приложения, и нет централизованного веб-сервера, на котором находится внутренняя логика.
Вместо этого вы можете использовать блокчейн для создания приложений на децентрализованной машине состояний (прим. Ethereum — это распределенная state machine. У этого понятия нет адекватного перевода. Используют понятия «конечный автомат» и «машина состояний»), которая поддерживается анонимными узлами в Интернете.
Под «машиной состояний» я подразумеваю машину, которая поддерживает некоторое заданное состояние программы и будущие состояния, разрешенные на этой машине. Блокчейны — это машины состояний, которые создаются с некоторым исходным состоянием и имеют очень строгие правила (т. е. консенсус), определяющие, как это состояние может переходить.
Более того, ни одна организация не контролирует этот децентрализованную машину состояний — он коллективно поддерживается всеми участниками сети.
А как насчет внутреннего сервера? Вместо того, чтобы контролировать серверную часть Medium, в Web 3.0 вы можете писать смарт-контракты, определяющие логику ваших приложений, и развертывать их на децентрализованной машине состояний. Это означает, что каждый человек, который хочет создать приложение блокчейна, развертывает свой код на этой общей машине состояний.
А фронтэнд? Он в значительной степени остается прежним, за некоторыми исключениями, о которых мы расскажем позже. Вот как выглядит архитектура:
Детальное рассмотрение
Теперь давайте углубимся в то, что делает это возможным.
1) Блокчейн
Блокчейн Ethereum часто называют «глобальным компьютером». Это потому, что это глобально доступная детерминированная машина состояний, поддерживаемый одноранговой сетью узлов. Изменения состояния на этом автомате управляются правилами консенсуса, которым следуют одноранговые узлы в сети.
Итак, другими словами, он буквально разработан как машина состояний, к которой любой человек в мире может получить доступ и написать. В результате эта машина принадлежит не какой-то одной организации, а коллективно всем участникам сети.
Еще одна вещь, которую нужно знать: данные могут быть записаны только в блокчейн Ethereum — вы никогда не сможете обновить существующие данные.
2) Смарт-контракты
Смарт-контракт — это программа, которая работает на блокчейне Ethereum и определяет логику изменений состояния, происходящих в блокчейне. Смарт-контракты написаны на языках высокого уровня, таких как Solidity или Vyper.
Поскольку код смарт-контракта хранится в блокчейне Ethereum, любой может проверить логику приложения всех смарт-контрактов в сети.
3) Виртуальная машина Ethereum (EVM)
Далее у вас есть виртуальная машина Ethereum, которая выполняет логику, определенную в смарт-контрактах, и обрабатывает изменения состояния, которые происходят на этой глобально доступной машине состояний.
EVM не понимает языки высокого уровня, такие как Solidity и Vyper, которые используются для написания смарт-контрактов. Вместо этого вы должны скомпилировать язык высокого уровня в байт-код, который затем может выполнить EVM.
4) Фронтэнд
Наконец, у нас есть фронтэнд. Как мы упоминали ранее, он определяет логику пользовательского интерфейса, но фронтэнд также взаимодействует с логикой приложения, определенной в смарт-контрактах.
Связь между фронтэндом и смарт-контрактами немного сложнее, чем показано на диаграмме выше. Давайте подробнее рассмотрим это далее.
Как фронтэнд взаимодействует со смарт-контрактами на Ethereum?
Мы хотим, чтобы наш фронтэнд взаимодействовал с нашими смарт-контрактами, чтобы они могли вызывать функции, но помните, что Ethereum — это децентрализованная сеть. Каждый узел в сети Ethereum хранит копию всех состояний машины состояний Ethereum, включая код и данные, связанные с каждым смарт-контрактом.
Когда мы хотим взаимодействовать с данными и кодом в блокчейне, нам нужно взаимодействовать с одним из этих узлов. Это связано с тем, что любой узел может транслировать запрос на выполнение транзакции на EVM. Затем майнер выполнит транзакцию и распространит полученное изменение состояния на остальную часть сети.
Есть два способа транслировать новую транзакцию:
Настройте свой собственный узел, на котором работает программное обеспечение блокчейна Ethereum.
Используйте узлы, предоставляемые сторонними сервисами, такими как Infura , Alchemy и Quicknode.
Если вы пользуетесь сторонним сервисом, вам не придется сталкиваться с головной болью, связаной с запуском полного узла самостоятельно. В конце концов, установка нового узла Ethereum на вашем собственном сервере может занять несколько дней. (Для синхронизации нужно много данных — он может даже потребовать больше пропускной способности и памяти, чем может выдержать обычный ноутбук.)
Кроме того, стоимость хранения полной цепочки блоков Ethereum растет по мере масштабирования вашего DApp, и вам нужно добавлять больше узлов для расширения вашей инфраструктуры. Вот почему по мере того, как ваша инфраструктура становится все более сложной, вам потребуются штатные DevOps инженеры. Они помогут вам поддерживать инфраструктуру, чтобы обеспечить надежную работу и быстрое время отклика.
Все, что нужно сказать, чтобы избежать этих проблем, — это то, почему многие DApps предпочитают использовать такие сервисы, как Infura или Alchemy, для управления инфраструктурой своих узлов. Конечно, есть компромисс, так как это создает централизованную точку входа, но давайте оставим эту “кроличью нору” на другой день.
Двигаясь далее, поговорим о провайдерах. Узлы, к которым вы подключаетесь, когда вам нужно взаимодействовать с блокчейном (независимо от того, настраиваете ли вы их самостоятельно или используете существующие от сторонних сервисов), часто называют «провайдерами».
Каждый клиент Ethereum (то есть провайдер) реализует спецификацию JSON-RPC. Это гарантирует наличие единого набора методов, когда внешние приложения хотят взаимодействовать с блокчейном. Если вам нужен учебник для начинающих по JSON-RPC, это упрощенный протокол удаленного вызова процедур (RPC) без сохранения состояния, который определяет несколько структур данных и правила их обработки. Он не зависит от транспорта, поэтому концепции могут использоваться в одном и том же процессе, через сокеты, через HTTP или во многих различных средах передачи сообщений. Он использует JSON (RFC 4627) в качестве формата данных.
Как только вы подключитесь к блокчейну через провайдера, вы сможете прочитать состояние, хранящееся в блокчейне. Но если вы хотите писать в состояние, вам нужно сделать еще одну вещь, прежде чем вы сможете отправить транзакцию в блокчейн — «подписать» транзакцию, используя свой закрытый ключ.
Например, представьте, что у нас есть DApp, которое позволяет пользователям читать или публиковать сообщения в блогах в блокчейне. У вас может быть кнопка на внешнем интерфейсе, которая позволяет любому запрашивать сообщения в блоге, написанные конкретным пользователем. (Напомним, что чтение из блокчейна не требует от пользователя подписи транзакции.)
Однако, когда пользователь хочет опубликовать новый пост в цепочке, наше DApp попросит пользователя «подписать» транзакцию, используя свой закрытый ключ — только тогда DApp передаст транзакцию в блокчейн. В противном случае узлы не приняли бы транзакцию.
Это «подписание» транзакций — это то, где обычно появляется Metamask .
Metamask — это инструмент, упрощающий приложениям управление ключами и подписание транзакций. Это довольно просто: Metamask хранит закрытые ключи пользователя в браузере, и всякий раз, когда внешнему интерфейсу требуется, чтобы пользователь подписал транзакцию, он вызывает Metamask.
Metamask также обеспечивает подключение к блокчейну (в качестве «провайдера»), поскольку у него уже есть подключение к узлам, предоставленным Infura, поскольку оно необходимо для подписи транзакций. Таким образом, Metamask является и поставщиком, и подписантом.
Хранение в блокчейне
Конечно, эта архитектура имеет смысл, если вы создаете приложение, в котором все смарт-контракты и данные полностью хранятся в блокчейне Ethereum. Но любой, кто создавал приложения на Ethereum, знает, что хранение всего в блокчейне становится очень дорогим очень быстро.
Имейте в виду, что с Ethereum пользователь платит каждый раз, когда добавляет новые данные в блокчейн. Это связано с тем, что добавление состояния в децентрализованную машину состояний увеличивает затраты для узлов, поддерживающих эту машину состояний.
Просить пользователей доплачивать за использование вашего DApp каждый раз, когда их транзакция требует добавления нового состояния, — не лучший пользовательский опыт. Один из способов борьбы с этим — использовать децентрализованное решение для хранения вне сети, такое как IPFS или Swarm .
IPFS — это распределенная файловая система для хранения и доступа к данным. Таким образом, вместо того, чтобы хранить данные в централизованной базе данных, система IPFS распределяет и хранит данные в одноранговой сети. Это позволяет вам легко получить его, когда вам нужно.
IPFS также имеет поощрительный уровень, известный как «Filecoin». Этот уровень стимулирует узлы по всему миру хранить и извлекать эти данные. Вы можете использовать провайдера, такого как Infura (который предоставляет вам узел IPFS) или Pinata (который предоставляет простой в использовании сервис, где вы можете «прикрепить» свои файлы к IPFS, взять хэш IPFS и сохранить его в блокчейне) .
Swarm похож на децентрализованную сеть хранения, но есть одно заметное отличие. В то время как Filecoin — это отдельная система, система поощрения Swarm встроена и обеспечивается с помощью смарт-контрактов на блокчейне Ethereum для хранения и извлечения данных.
Итак, теперь с IPFS или Swarm архитектура нашего приложения выглядит так:
Внимательные читатели могли также заметить на диаграмме ниже, что код внешнего интерфейса не хранится в блокчейне. Мы могли бы разместить этот код на AWS, как обычно в Web 2.0, но это создает узкую точку централизации для вашего DApp. Что, если AWS выйдет из строя? Что, если он подвергнет ваше приложение цензуре?
Вот почему, если вы хотите создать действительно децентрализованное приложение, вы можете разместить свой интерфейс в децентрализованном решении для хранения, таком как IPFS или Swarm.
Итак, теперь архитектура вашего приложения выглядит примерно так:
Запросы к блокчейну
До сих пор мы говорили о том, как писать в блокчейн, подписывая транзакции и затем отправляя их в блокчейн. Но как насчет чтения данных из смарт-контрактов в блокчейне? Есть два основных способа сделать это:
1. События смарт-контракта
Вы можете использовать библиотеку Web3.js для запроса и прослушивания событий смарт-контракта. Вы можете прослушивать определенные события и указывать обратный вызов каждый раз, когда событие запускается. Например, если у вас есть смарт-контракт, который отправляет непрерывный поток платежей от человека А человеку Б в каждом блоке, вы можете генерировать событие каждый раз, когда новый платеж делается человеку Б. Код вашего фронтэнда может прослушивать запускаемые события смарт-контрактом и выполнять определенные действия на его основе.
2. The Graph
Описанный выше подход работает, но имеет некоторые ограничения. Например, что, если вы развернете смарт-контракт, а позже поймете, что вам нужно сгенерировать событие, которое вы изначально не включили? К сожалению, вам придется повторно развернуть новый смарт-контракт с этим событием и данными. Более того, использование обратных вызовов для обработки различной логики пользовательского интерфейса очень быстро становится очень сложным.
Вот тут-то и появляется The Graph.
The Graph — это решение для индексации вне сети, которое упрощает запрос данных в блокчейне Ethereum. The Graph позволяет вам определить, какие смарт-контракты индексировать, какие события и вызовы функций прослушивать и как преобразовывать входящие события в сущности, которые может использовать логика вашего фронтэнда (или что-то еще, использующее API). Он использует GraphQL в качестве языка запросов, который нравится многим фронтенд-инженерам из-за его выразительности по сравнению с традиционными REST API.
Индексируя данные блокчейна, The Graph позволяет запрашивать данные в цепочке в логике нашего приложения с низкой задержкой.
Теперь ваша архитектура DApp выглядит так:
Мы почти закончили, но у нас осталась одна важная тема: масштабирование.
Масштабирование вашего децентрализованного приложения
Как вы, возможно, слышали, Ethereum не масштабируется — по крайней мере, пока.
Средняя цена газа Эфириума
Средняя комиссия за транзакцию
Средний размер блока
Очевидно, у нас здесь есть проблема. Создание DApp на Ethereum с высокой комиссией за газ и полными блоками приводит к очень плохому UX. К счастью, некоторые решения находятся в стадии разработки.
Одним из популярных решений для масштабирования является Polygon, решение для масштабирования L2. Вместо выполнения транзакций в основной цепочке блоков у Polygon есть «сайдчейны», которые обрабатывают и выполняют транзакции. Сайдчейн — это вторичный блокчейн, который взаимодействует с основной цепочкой. Время от времени боковая цепь отправляет совокупность своих последних блоков обратно в основную цепь.
Другими примерами решений L2 являются Optimistic Rollups и zkRollups . Идея здесь аналогична: мы пакетируем транзакции вне сети, используя «сводный» смарт-контракт, а затем периодически фиксируем эти транзакции в основной цепочке.
Основная идея такова: решения L2 выполняют транзакцию (то есть медленную часть) вне цепочки, при этом в цепочке хранятся только данные транзакции. Это позволяет нам масштабировать блокчейн, потому что нам не нужно выполнять каждую транзакцию в цепочке. Это также делает транзакции более быстрыми и дешевыми — и они все еще могут взаимодействовать с основным блокчейном Ethereum, когда это необходимо.
Собираем все вместе
Если от всего этого у вас кружится голова, вы не одиноки. Объединение всех этих инструментов воедино является сложной задачей и может привести к болезненному опыту разработчика. Но не волнуйтесь — мы начинаем видеть новые платформы для разработчиков, которые действительно улучшают работу разработчиков.
Например, Hardhat — это среда разработки, которая упрощает разработчикам Ethereum создание, развертывание и тестирование своих смарт-контрактов. Hardhat предлагает «Сеть Hardhat», которую разработчики могут использовать для развертывания своих смарт-контрактов в локальной сети — без необходимости иметь дело с реальными средами. Более того, он предлагает отличную экосистему плагинов , которая значительно облегчает жизнь разработчикам. Hardhat также предоставляет функциональность console.log(), похожую на javascript, для целей отладки.
Конечно, это только начало. Я надеюсь, что в будущем мы продолжим видеть лучшие инструменты для разработчиков.
Вывод
Большинство людей тратят месяцы на то, чтобы понять, как работает цепочка инструментов, поэтому, если вы новичок в разработке DApp, я надеюсь, что эта статья сэкономила вам время. Пора строиться!