Щоб побудувати власний додаток на основі Ethereum доведеться вирішити кілька завдань по розробці архітектури і структури програми, адже в традиційній клієнт-серверної моделі з’являється новий компонент – блокчейн.
У цій статті розглянемо найбільш поширені сценарії розробки Ethereum-додатків, в основі яких лежать різні моделі взаємодії між цими компонентами. Розповімо про безсерверні додатки, плагіни для браузера, приватні вузли, офлайн-підписки і інші важливі питання, що виникають при проектуванні структури додатків.
Клієнт-блокчейн в безсерверних додатках
Серед додатків для Ethereum широко поширена безсерверна архітектура, в рамках якої весь обмін даними здійснюється між клієнтом і блокчейном.
В такому разі, перш за все, необхідно передати користувачам код клієнта. Найпростіше це зробити через статичний сайт з веб-додатком, що підтримує API web3. У ролі хостингу може виступати як AWS S3, Google Cloud, Github Pages або інші хмарні провайдери, так і ваш власний сервер. Більш того, якщо ви можете розраховувати на широку підтримку протоколів bzz або ipfs серед користувачів, то для повної децентралізації код можна поширювати через Swarm або IPFS.
Відправка запитів до блокчейну
Наступний крок – читання даних з блокчейна. Як ви вже знаєте, для цього необхідно з’єднання з активним вузлом мережі Ethereum. За встановлення з’єднання з вузлом відповідає web3-провайдер.
Можливо, хтось з ваших користувачів вже встановив з’єднання з вузлом через офіційний клієнт Mist або з допомогою браузерного розширення, наприклад, популярного Metamask, яке виступає в ролі легкого клієнта для взаємодії з блокчейном. На його сторінці найчастіші запитання наведено приклад коду, який перевіряє, чи доступний клієнту web3, і задає Metamask в якості провайдера.
Як же бути з користувачами, які не встановили Mist або Metamask? Якщо вам потрібно лише надати їм можливість зчитувати дані з блокчейну, не здійснюючи транзакцій, то ви можете встановити з’єднання з публічним вузлом мережі Ethereum. Такий вузол повинен працювати через клієнт geth або parity і перебувати у відкритому доступі, але не повинен розголошувати API для керування закритими ключами або зберігати паролі і сесії авторизації. Цей вузол буде грати роль інтерфейсу для виклику так званих постійних функцій, які не змінюють внутрішнього стану контракту. Якщо вам не хочеться запускати такий вузол самостійно, можете звернутися до безкоштовних публічних вузлів, які люб’язно надає команда [Infura](https://infura.io/).
Таким чином, власники систем з робочою інфраструктурою Ethereum можуть скористатися з’єднанням з власними надійними вузлами, а менш просунуті користувачі можуть звернутися до публічних вузлів. У такому разі вони з власної волі довіряють інформації, наданою чужими вузлами, заради простоти використання програми.
Відправка транзакцій
Читання даних з блокчейну — не найважча задача, але як бути, якщо ви хочете, щоб користувачі здійснювали операції і дії, які впливають на стан смарт-контрактів?
Для користувачів, які встановили Mist або Metamask, проблема вже вирішена: обидва інтерфейсу дозволяють управляти обліковими записами і підписувати транзакції за запитом програми. Для цього Mist надає доступ до локального вузла і його акаунтів. Metamask, в свою чергу, підписує транзакції на стороні клієнта і транслює їх публічним вузлам Metamask.
Підтвердження транзакції в Metamask.
Якщо ваш додаток не вимагає установки Metamask або Mist, то для роботи з ним іншим користувачам доведеться відправляти транзакції вручну з будь-якого звичного їм гаманця.
У більшості додатків це здійснюється так: користувач отримує запит на відправку певної суми ETH на деяку адресу — можливо, разом з QR-кодом або опцією копіювання в буфер обміну.
Діалогове вікно обміну в Shapeshift
Потім ваш додаток може відстежувати події контракту з боку клієнта, щоб оновити інтерфейс, як тільки транзакція, відправлена без додатку, буде виконана. Оскільки відстеження подій — це не що інше, як читання даних з блокчейну, його можна здійснювати через публічний вузол.
Для того щоб виконувати функції в рамках контракту, від користувача вам можуть знадобитися не тільки ETH, але і додаткові дані. Для цього у функцій контракту є метод request, за допомогою якого можна легко отримати необхідні для їх виконання дані та відобразити їх користувачу разом з адресою одержувача.
SimpleToken.at(tokenAddress).transfer.request(«0xbcfb5e3482a3edee72e101be9387626d2a7e994a», 1e18).params[0].data // повертає дані для виклику функції переказу з заданими аргументами = > '0xa9059cbb00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000de0b6b3a7640000'
Це не дуже хороший підхід, оскільки він вимагає від користувача досить складних дій. Далеко не всі розуміють, як влаштовані додаткові дані Ethereum, і, тим більше, як відправляти їх разом з транзакціями.
Якщо ви все ж вирішите скористатися цим методом, переконайтеся, що fallback-функція контракту відхиляє всі платежі, або ж за замовчуванням обробляє транзакції так, щоб користувач не втратив кошти, забувши вказати додаткові дані при відправці вручну.
До речі, нерідко добре продумана запасна функція відправляє транзакцію саме так, як того очікує користувач. У такому випадку ваш контракт просто реагує на відправку коштів, виконуючи ті чи інші функції в залежності від поточного стану, і користувачам не потрібно вказувати ніяких додаткових даних.
Ще одна хитрість – проксі-контракти, які виконують окремі функції в рамках основного контракту, як тільки отримують ефір. В якості простого прикладу припустимо, що ви розробляєте додаток для голосування з двома варіантами вибору в основному контракті – «за» або «проти». Замість того щоб просити користувача включити в транзакцію позначку «за» або «проти», ви можете створити два додаткових контракти, які будуть відповідати лише за те, щоб голосувати за або проти в рамках основного контракту від імені відправників отриманих ними транзакцій. Природно, таке рішення підходить тільки для немудрих додатків, зате часто воно спрощує їх використання для тих, чиї браузери не налаштовані для роботи з блокчейном Ethereum.
Створення власного гаманця
Є ще один спосіб дати користувачам можливість здійснювати складні операції зі смарт-контрактами: ви можете вбудувати функції управління гаманцем в вашу програму. Спочатку програма створить для користувача новий рахунок, через який будуть проходити транзакції, надіслані безпосередньо з вашої програми.
Coral Fundraiser запитує пароль від нового рахунку та завантажує його на комп’ютер користувача в зашифрованому вигляді для подальшого доступу з іншої сесії або навіть з іншого гаманця.
Потім користувачеві потрібно буде відправити на цей рахунок деяку первісну суму ефіру з іншого гаманця або рахунки на біржі. Щоб спростити цей крок, можна передбачити інтеграцію з Shapeshift. Як тільки на рахунок надійде достатньо коштів для оплати комісій, клієнтський код в один клік виконає всі необхідні операції від імені користувача.
«Під капотом» ваш додаток використовує закритий ключ від створеного облікового запису, щоб підписати транзакції на стороні клієнта, а потім передасть їх публічному вузлу для виконання.
Щоб реалізувати цю ідею, доведеться написати чимало коду, адже вам потрібно буде додати можливості створення, шифрування та імпортування Ethereum-аккаунта (у цьому вам може допомогти бібліотека ethereum-wallet). Більш того, всі транзакції повинні бути сформульовані і підписані вручну, а потім відправлені вузлу у серіалізованому форматі. Ці завдання вимагають активної участі користувача: він повинен створити і налаштувати новий рахунок і надійно зберегти файл з його даними.
Тим не менш, як тільки користувач впорається з первісними налаштуваннями, він зможе легко відправляти транзакції в блокчейн Ethereum безпосередньо з вашого додатку без необхідності встановлювати додаткові програми.
В кінцевому підсумку те, який підхід ви оберете, буде залежати від того, на яких користувачів ви орієнтуєтеся, яким чином вони будуть взаємодіяти з вашим додатком, і як часто вони будуть до нього звертатися – постійно, зрідка або лише одного разу.
З’єднання між сервером і блокчейном
Тепер додамо до нашої архітектури сервер і на час забудемо про клієнта у цьому розділі. Все нижче сказане відноситься не тільки до серверів додатків, але і до автономних додатків, скриптів і процесів обробки даних.
Налаштування локального вузла
Перше рішення найпростіше: ви можете запустити локальний вузол Ethereum і використовувати його інтерфейс JSON-RPC для здійснення всіх операцій в блокчейні.
Також ви можете завести заздалегідь розблокований рахунок, щоб відправляти через нього транзакції з програми (для цього і в Geth, і в Parity передбачений прапорець «unlocked»). Тільки обов’язково переконайтеся, що доступ до інтерфейсу JSON-RPC даного вузла відкритий виключно для вашого додатку, інакше хто завгодно зможе отримати доступ до вузла і вкрасти ваші кошти. В якості додаткової обережності зберігайте на розблокованому рахунку як можна менші суми і поповнюйте його з інших джерел по мірі необхідності.
Офлайн-підписи і публічні вузли
Друге можливе рішення схоже на те, що ми розглядали в попередньому розділі: ваш додаток може підписувати транзакції офлайн і передавати їх публічному вузлу для обробки.
Вибираючи це рішення, пам’ятайте, що в цьому випадку ви сліпо довіряєте публічному вузлу, з яким ви встановлюєте з’єднання. Незважаючи на те що вузол не може вносити зміни до надісланих йому транзакцій, він може не переслати їх у мережу або підробити відповіді на ваші запити. Цей ризик можна знизити, встановивши з’єднання з декількома вузлами одночасно і відправляючи їм всім одні й ті ж операції і запити. Проте це помітно ускладнить ваш код.
Будуємо архітектуру додатку
Ми обговорили різні способи відправки запитів і транзакцій від клієнта або сервера до блокчейну. Тепер саме час поговорити про те, як зібрати всі компоненти воєдино.
Координація клієнта і сервера
Одночасна взаємодія клієнта і сервера з блокчейном може зажадати координації їх дій. Наприклад, вам може знадобитися, щоб сервер реагував на будь-яку дію клієнта в блокчейні або щоб клієнт відобразив зміни в стані контракту.
Найпоширеніший спосіб спостерігати за змінами в контракті як у клієнта, так і на сервері — це відстежувати події контракту. Ретельно продумуйте їх встановлення так, щоб кожній очікуваній дії відповідала певна подія, і індексуйте аргументи, щоб обробники подій могли фільтрувати лише потрібні їм події. Як правило, клієнт має відслідковувати тільки ті події, які безпосередньо відносяться до його роботи, тоді як сервер може відстежувати всі контракти, пов’язані з вашим додатком.
Крім того, можна окремо відстежувати конкретні транзакції, надіслані безпосередньо з вашої програми, щоб підтвердити їх успішне завершення.
Також клієнт може передавати ID відправленої їм транзакції на сервер в якості доказу дії, вчиненого в блокчейні. Таким чином, серверу не доведеться відстежувати всі події. Проте майте на увазі, що зловмисники можуть спостерігати за транзакціями в блокчейні і відправляти на сервер підроблені ID транзакцій від імені клієнта. Переконайтеся, що сервер сприймає повідомлення від клієнта повідомлення, а не як надійне джерело інформації.
ePayments – поповнення, виведення криптовалюти в фіат, карта Mastercard
Незалежно від того, чи відстежуються транзакції і події, реагувати на них слід тільки після отримання достатньої кількості підтверджень. Навіть якщо транзакція вже була обчислена майнерами, через реорганізацію блокчейну вона може здійснитися в іншому контексті і потенційно виявитися недійсною. Дочекайтеся обробки приблизно 12 блоків (або більшого числа тестової мережі) перед тим, як діяти у відповідь на подію з блокчейна. Тим не менш, щоб тримати користувача в курсі, ви можете повідомити йому, що транзакція була відправлена, але ще не була підтверджена.
Завдання сервера
Вам потрібно відповісти на важливе питання: для чого потрібен сервер? У традиційних клієнт-серверних додатках сервер грає роль сховища даних, відповідає за бізнес-логіку і координує роботу клієнтів. Тепер всі ці завдання можна вирішувати в блокчейні.
Тим не менш, сервер може виявитися вельми корисним для вашої програми. По-перше, код, виконуваний в блокчейні, не може напряму працювати з офчейн-сервісами. А значить, якщо вам потрібна інтеграція зі сторонніми сервісами, імпорт курсу USD/ETH і інших даних із зовнішніх джерел або можливість відправки електронної пошти, то вам не обійтися без сервера.
Сервер також може кешувати або індексувати ваші смарт-контракти. Блокчейн як і раніше буде відігравати роль головного джерела інформації, але в той же час клієнти зможуть звертатися до сервера для пошуку даних, а потім підтверджувати їх в блокчейні.
В наші дні зберігання великих обсягів даних в мережі Ethereum коштує неймовірно дорого через великі витрати газу при роботі з сховищами контрактів. Тому ваш додаток може використовувати сервер для зберігання великих масивів даних і зберігати в блокчейні тільки хеші, необхідні для підтвердження. Те ж саме відноситься до складних обчислень, оскільки вони можуть перевищити встановлений в мережі Ethereum ліміт газу на блок, краще здійснювати їх у окремій інфраструктурі.
Примітно, що з’являється все більше проектів, що забезпечують плавну інтеграцію з EVM для вирішення цих завдань. Наприклад, Filecoin і Storj призначені для зберігання даних, Truebit — для обчислень, а Oraclize — для роботи з даними з сторонніх мереж. Можливо, з часом на серверах буде виконуватися все менше процесів, доки їм на зміну остаточно не прийде безліч сайдчейнів і інтеграцій. Не виключено, що через кілька років ця стаття застаріє, а наші блокчейн-програми стануть по-справжньому децентралізованими.