Создание архитектуры Ethereum-приложения

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

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

Клиент-блокчейн в бессерверных приложениях

Среди приложений для Ethereum широко распространена бессерверная архитектура, в рамках которой весь обмен данными осуществляется между клиентом и блокчейном.

В таком случае, прежде всего, необходимо передать пользователям код клиента. Проще всего это сделать через статический сайт с веб-приложением, поддерживающим API web3. В роли хостинга может выступать как AWS S3, Google Cloud, Github Pages или другие облачные провайдеры, так и ваш собственный сервер. Более того, если вы можете рассчитывать на широкую поддержку протоколов bzz или ipfs среди пользователей, то для полной децентрализации код можно распространять через Swarm или IPFS.

Отправка запросов к блокчейну

Следующий шаг – чтение данных из блокчейна. Как вы уже знаете, для этого необходимо соединение с активным узлом сети Ethereum. За установку соединения с узлом отвечает web3-провайдер.

Возможно, кто-то из ваших пользователей уже установил соединение с узлом через официальный клиент Mist или с помощью браузерного расширения, например, популярного Metamask, которое выступает в роли лёгкого клиента для взаимодействия с блокчейном. На его странице часто задаваемых вопросов приведён пример кода, который проверяет, доступен ли клиенту web3, и задаёт Metamask в качестве провайдера.

Создание архитектуры Ethereum-приложения


Как же быть с пользователями, не установившими Mist или Metamask? Если вам нужно лишь предоставить им возможность считывать данные из блокчейна, не совершая транзакций, то вы можете установить соединение с публичным узлом сети Ethereum. Такой узел должен работать через клиент geth или parity и находиться в открытом доступе, но не должен разглашать API для управления закрытыми ключами или сохранять пароли и сессии авторизации. Этот узел будет играть роль интерфейса для вызовa так называемых постоянных функций, не изменяющих внутреннего состояния контракта. Если вам не хочется запускать такой узел самостоятельно, можете обратиться к бесплатным публичным узлам, которые любезно предоставляет команда [Infura](https://infura.io/).

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

Отправка транзакций

Чтение данных из блокчейна — не самая трудная задача, но как быть, если вы хотите, чтобы пользователи совершали транзакции и действия, которые влияют на состояние смарт-контрактов?

Для пользователей, установивших Mist или Metamask, проблема уже решена: оба интерфейса позволяют управлять аккаунтами и подписывать транзакции по запросу приложения. Для этого Mist предоставляет доступ к локальному узлу и его аккаунтам. Metamask, в свою очередь, подписывает транзакции на стороне клиента и транслирует их публичным узлам Metamask.


Подтверждение транзакции в Metamask. Источник: Developing Ethereum Dapps with Truffle and MetaMask

Если же ваше приложение не требует установки Metamask или Mist, то для работы с ним остальным пользователям придётся отправлять транзакции вручную из любого привычного им кошелька.

В большинстве приложений это осуществляется так: пользователь получает запрос на отправку определённой суммы ETH на некоторый адрес — возможно, вместе с QR-кодом или опцией копирования в буфер обмена.


Диалоговое окно обмена в Shapeshift

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

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

SimpleToken.at(tokenAddress).transfer.request(«0xbcfb5e3482a3edee72e101be9387626d2a7e994a», 1e18).params[0].data</p><br /><br /><br />
<p>// возвращает данные для вызова функции перевода с заданными<br /><br /><br /><br />
аргументами => ‘0xa9059cbb00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000de0b6b3a7640000’


Мне не очень нравится такой подход, поскольку он требует от пользователя достаточно сложных действий. Далеко не все понимают, как устроены дополнительные данные в Ethereum, и, тем более, как отправлять их вместе с транзакциями.

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

Кстати, нередко хорошо продуманная запасная функция отправляет транзакцию именно так, как того ожидает пользователь. В таком случае ваш контракт просто реагирует на отправку средств, выполняя те или иные функции в зависимости от текущего состояния, и пользователям не нужно указывать никаких дополнительных данных.

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

Создание собственного кошелька

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


Coral Fundraiser запрашивает пароль от нового счёта и скачивает его на компьютер пользователя в зашифрованном виде для последующего доступа из другой сессии или даже из другого кошелька

Затем пользователю нужно будет отправить на этот счёт некоторую первоначальную сумму эфира с другого кошелька или счёта на бирже. Чтобы упростить этот шаг, можно предусмотреть интеграцию с Shapeshift. Как только на счёт поступит достаточно средств для оплаты комиссий, клиентский код в один клик выполнит все необходимые операции от имени пользователя.

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

Чтобы реализовать эту идею, придётся написать немало кода, ведь вам нужно будет добавить возможности создания, шифрования и импортирования Ethereum-аккаунта (в этом вам может помочь библиотека ethereum-wallet). Более того, все транзакции должны быть сформулированы и подписаны вручную, а затем отправлены узлу в сериализованном формате. Эти задачи требуют активного участия пользователя: он должен создать и настроить новый счёт и надёжно сохранить файл с его данными.

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

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

Соединение между сервером и блокчейном

Теперь добавим к нашей архитектуре сервер и на время забудем о клиенте в этом разделе. Всё нижесказанное относится не только к серверам приложений, но и к автономным приложениям, скриптам и процессам обработки данных.

Настройка локального узла

Первое решение самое простое: вы можете запустить локальный узел Ethereum и использовать его интерфейс JSON-RPC для осуществления всех операций в блокчейне.



Также вы можете завести заранее разблокированный счёт, чтобы отправлять через него транзакции из приложения (для этого и в Geth, и в Parity предусмотрен флаг «unlocked»). Только обязательно убедитесь, что доступ к интерфейсу JSON-RPC данного узла открыт исключительно для вашего приложения, иначе кто угодно сможет получить доступ к узлу и украсть ваши средства. В качестве дополнительной предосторожности храните на разблокированном счёте как можно меньшие суммы и пополняйте его из других источников по мере необходимости.

Офлайн-подписи и публичные узлы

Второе возможное решение похоже на то, что мы рассматривали в предыдущем разделе: ваше приложение может подписывать транзакции офлайн и передавать их публичному узлу для обработки. О том, как настроить провайдер web3 в конфигурации Truffle для того, чтобы прозрачно подписывать транзакции офлайн и отправлять их узлу Infura, вы можете узнать из этой статьи.



Выбирая это решение, помните, что в этом случае вы слепо доверяете публичному узлу, с которым устанавливаете соединение. Несмотря на то что узел не может вносить изменения в отправленные ему транзакции, он может не переслать их в сеть или подделать ответы на ваши запросы. Этот риск можно снизить, установив соединение с несколькими узлами одновременно и отправляя им всем одни и те же транзакции и запросы. Однако это заметно усложнит ваш код.

Строим архитектуру приложения

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

Координация клиента и сервера

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

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

Кроме того, можно отдельно отслеживать конкретные транзакции, отправленные непосредственно из вашей программы, чтобы подтвердить их успешное завершение.



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

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

Задачи сервера

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

Тем не менее, сервер может оказаться весьма полезным для вашего приложения. Во-первых, код, выполняемый в блокчейне, не может напрямую работать с офчейн-сервисами. А значит, если вам потребуется интеграция со сторонними сервисами, импорт курса USD/ETH и других данных из внешних источников или возможность отправки электронной почты, то вам не обойтись без сервера.

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

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

Примечательно, что появляется всё больше проектов, обеспечивающих плавную интеграцию с EVM для решения этих задач. Например, Filecoin и Storj предназначены для хранения данных, Truebit ?— для вычислений, а Oraclize — для работы с данными из сторонних сетей. Возможно, со временем на серверах будет выполняться всё меньше процессов, пока им на смену окончательно не придёт бесчисленное множество сайдчейнов и интеграций. Не исключено, что через несколько лет этот пост устареет, а наши блокчейн-приложения станут по-настоящему децентрализованными.
Источник https://cryptocurrency.tech/
При копировании ссылка http://elitetrader.ru/index.php?newsid=388199 обязательна
Условия использования материалов