Еволюція airdrop: від звичайного спаму до Merkle tree
Airdrop - популярний спосіб роздати маркери проекту спільноті. Настільки популярний, що часом призводить до заторів в мережі Ethereum і величезних комісій в транзакціях. Зовсім недавно подібна механіка, яка використовувалася для голосування на біржі FCoin, привела до сумних наслідків для всієї мережі Ethereum.
Хороший алгоритм дозволяє вирішити відразу кілька проблем смарт-контрактів, які оперують великими списками адрес користувачів. Справа в тому, що покласти в контракт список навіть з кількох тисяч адрес і дозволити цьому набору адрес щось робити в контракті безпосередньо, не вийде - в блокчейні економиться кожен байт і це занадто дорого.
Для вирішення завдання необхідно в коді контракту визначити, чи належить дана адреса списку «білих» адрес. Якщо так, то дозволити провести потрібну дію. Пропонований варіант дозволяє достатньо просто вирішити цю проблему, тримаючи в контракті всього одне число. Алгоритм блискучого криптографа Ральфа Меркле широко використовується в практично будь-якому децентралізованому софті для забезпечення цілісності наборів даних.
Airdrop та ACL
Є така практика - випустити свій токен і розіслати невеликі його кількості по десяткам тисяч адрес, у яких на балансі є, для прикладу, хоча б 1 ETH. Такий спам вкрай популярний зараз для просування власних токенів. Мало кому подобається поява на своєму балансі чиїхось невідомих токенів. Проте, проектам це потрібно, і замовлення на airdrop дуже популярні. Робиться це звичайно по-старому, ось так (на прикладі Ethereum, але підходять і інші блокчейни з контрактами, EOS, наприклад):
- парсять блокчейн і знаходять багато адрес за заданими критеріями;
- створюють адресу, на якому зберігається достатньо токенів для розсилки всім зі списку, або передбачають в контракті токена можливість створювати потрібну кількість токенів заданою адресою (з відправкою транзакції зі спеціальної привілейованої адреси);
- кладуть на цю адресу достатньо Ethereum, щоб вистачило на оплату комісії за кожну відправку токенів користувачеві;
- запускають скрипт розсилки, який перебирає адреси і для кожного створює транзакцію, що переводять токени за заданою адресою.
Тобто це просто прохід по купі адрес і розсилка токенів кожному. У децентралізованих системах така push-стратегія зазвичай достатньо кульгава, коштує дорого, породжує діри в безпеці і взагалі - це спам. У числі її недоліків:
- на комісії йде багато коштів (чим більше адрес, тим більше комісія). Крім того, в момент розсилки комісія може рости, тому що зростає навантаження на мережу, вартість транзакції зростає теж;
- розсилка вимагає написання скрипта, який буде слати транзакції, а в скрипті зашитий секретний ключ, який дає доступ до купи токенів;
- розсилку необхідно запрограмувати і вміти продовжити з місця, де все зламалося.
При цьому є рішення набагато простіше, яке, як прийнято в децентралізованих мережах, більшу частину роботи віддає софту на стороні клієнта. Це контроль доступу з використанням дерева Меркле - вкрай зручної структури даних, щоб зберігати в контракті лише одне число фіксованого розміру (merkleRoot), яке містить в собі інформацію про всі дані, включені в дерево (про величезний список адрес одержувачів, наприклад).
Жодної магії тут немає: інформацію, яка доводить, що адреса є в списку дозволених, клієнтський код надає самостійно, роблячи відносно об'ємні обчислення, і позбавляючи контракт від необхідності перегляду величезного списку адрес. Структура дерева Меркле вкрай корисна для безлічі завдань.
Так, цей алгоритм годиться для створення величезних ACL (Access Control List), які дозволяють надати доступ до деякої функції контракту мільйонам акаунтів. Для цього потрібно записати в контракт одне число для перевірки приналежності аккаунту до списку.
Розглянемо схему з airdrop, тому що вона зараз вкрай затребувана на ринку і є простим і демонстративним прикладом смарт-контрактів з великими ACL.
Схема DApp-a
Для початку варто нагадати, що смарт-контракт спочатку деплоїться в мережу Ethereum, після чого отримує свою власну адресу і баланс в мережі, а далі бере і процесує всередині себе вхідні транзакції.
У схемі merkle-airdrop токени не надсилали за адресами, а користувачі самі «вимагають» свої маркери, відправляючи транзакції в контракт і оплачуючи комісію. Секрет в тому, що в транзакцію користувачі додають дані, що дозволяють контрактам легко перевірити, чи знаходяться вони в «білому» списку адрес, причому, контрактам зовсім не обов'язково пам'ятати сам список. Сам контракт при цьому є вкрай економним, йому необхідно всього лише одне (!) число для списку адрес будь-якого (!) розміру - саме в цьому суть цього відмінного алгоритму. А ще такий контракт вкрай простий в запуску, після якого не вимагається взагалі ніякої підтримки, і, за рахунок цієї простоти, ще й вкрай безпечний у використанні. Але про ці моменти трохи пізніше.
Смарт-контракт
Ось як приблизно працює вся схема з контрактом merkle-airdrop:
- У мережу викладається (або вже присутній там) контракт токена.
- Створюється список адрес, яким дозволено «вимагати» свої маркери. Список генерується на основі аналізу блокчейна, збирається на сайті або іншим способом.
- Всі адреси поміщаються в один досить масивний файл, який буде доступний публічно за деяким URL. Наприклад, можна помістити його в протокол IPFS, але можна використовувати будь-який спосіб залити файл і прочитати його потім з клієнтського браузера.
- На файл «нацьковується» скрипт, який з усіх вміщених в ньому адрес будує Merkle tree, і отримує один єдиний хеш - Merkle root. Цей root - головний параметр нашого контракту, який буде видавати маркери.
- Контракт merkle-airdrop викладається в мережу. При викладенні він запам'ятовує merkle root і адресу контракту-токена, щоб він міг робити трансфер цього токена одержувачам.
- На баланс airdrop-контракту кладеться велика кількість токенів (звичайним трансфером), достатня щоб видати всім учасникам списку.
- Тепер у airdrop-контракту доступна зовнішня функція claim-tokens-by-merkle-proof, яка приймає на вхід від користувача merkle-proof - доказ того, що користувач є в тому самому великому «білому» списку адрес. Якщо доказ вірний, то контракт виконує transfer () зі своєї адреси і переводить користувачу токени.
Користувач
А ось що відбувається в браузері клієнта (всі операції проводяться клієнтським JavaScript прямо на сторінці DApp-a):
- Знаходить свою адресу в «білому» списку, який публічно розміщений десь в Мережі (наприклад в IPFS) і переконується, що має право на токени.
- Будує з усіх адрес в списку дерево Меркле, і з нього отримує той самий merkle-proof, який являє собою кілька чисел (хешів), їх кількість залежить від розміру списку, але в порівнянні з ним дуже мало, наприклад для ~ 100 000 адрес вистачить ~ 17 хешів (розмір merkle-proof: log2N, де N - число елементів).
- Відправляє в контракт транзакцію, що викликає функцію claim-tokens-by-merkle-proof і відправляє туди отриманий merkle-proof. Контракту залишається перевірити proof (за допомогою збереженого merkle-root) і зробити transfer () токенів, якщо proof вірний.
- Контракт запам'ятовує, кому він видав токени, щоб запобігти повторній видачі.
Загальними словами merkle-proof можна описати як «шлях, яким можна пройти від адреси користувача по дереву Меркле до самого merkle-root». Це досить серйозна обчислювальна робота і зайві дані, які ми таким чином винесли з контракту, заощадивши найдорожче в блокчейні - сховище.
Merkle-proof доказ складається з log2N хешів (з округленням вгору до цілого). Кожен хеш - такого ж розміру як і merkle-root, який ми записали в контракт-airdrop. Тобто для списку з 1024 адрес, користувач повинен надати 10 хешів, а для ~ 4 млрд адрес - всього 32 хешу. Саме в протоколі побудови і пред'явлення доказів і ховається основна «фішка» контракту - зберігання мінімальної кількості інформації для визначення приналежності деяких даних до дуже великого списку. Причому, чим список більший, тим більший виграш.
В реальності контракт доповнюється ще й можливістю забрати невикористані токени, оновити merkle root і ввести обмеження за часом, наприклад заборонити видачу токенів після деякого часу. Контракт нескладно допрацьовується для роздачі довільної кількості токенів кожній адресі, в цьому випадку в файлі зберігаються не просто адреси отримувачів, а й необхідні суми токенів, і злегка допрацьовується функція для перевірки merkle-proof, але загальний алгоритм від цього майже не змінюється.
Переваги та недоліки merkle-airdrop
Окремо можна виділити переваги і недоліки вищевказаного методу в порівнянні з традиційною розсилкою скриптом:
Плюси:
- транзакція, що вимагає токени, коштує мало газу, причому це число - константа, що залежить від розміру «білого» списку;
- після запуску контракту він не вимагає жодної підтримки, вся активність забезпечується користувачами;
- дозволяє працювати зі списками практично довільного розміру з мінімальним витрачанням storage блокчейну.
Мінуси:
- треба кудись викладати публічний список адрес;
- клієнтському коду необхідно переглянути всі адреси в «білому» списку і виконати досить ресурсномісткий код.
В даному алгоритмі немає ніяких секретів, такий виграш по пам'яті контракту щедро «оплачений» роботою коду на стороні клієнта з перевірки приналежності до списку. Такий підхід вкрай вдало демонструє відмінність моделей використання смарт-контрактів в порівнянні з традиційними централізованими системами.
Традиційна розсилка токенів скриптом у відповідь може протиставити лише просту і зрозумілу схему роботи. А зусилля програміста для запуску звичайного airdrop в рази перевищують зусилля для викладення merkle-airdrop контракту, запущений скрипт треба моніторити, щоб не відвалився посередині списку, щоб у нього не закінчилися кошти на комісії за транзакції, стежити, щоб ніхто не вкрав ключ, яким скрипт підписує транзакції. Плюс, якщо у вас є файл з адресами, власне програміст вам і не потрібен - деплой такого контракту вкрай просто здійснити через публічні сервіси.
Особливості реалізації
Крім основного смарт-контракту, повний DApp для merkle-airdrop має деякі особливості в реалізації. У схемі merkle-airdrop велика кількість роботи покладається на код в браузері користувача, наприклад побудова merkle proof, для якого потрібно пробігти по всьому списку адрес. Сам список адрес треба десь публічно зберігати, а для користувача це повинно бути не складніше ніж залити файл на сервер.
Тому в конструкторі merkle-airdrop на платформі Smartz розробникам довелося реалізувати завантаження списку адрес в IPFS, віддачу цього списку клієнту і зробити для всього цього спеціальні JS-віджети для роботи з завантаженим в IPFS файлом з адресами. У фінальній версії DApp-а планується зберігати інформацію про IPFS безпосередньо в контракті, щоб мати 100% необхідних для airdrop даних в блокчейні. На даний момент випущена робоча alpha-версія, яка без проблем вміє роздавати токени за великим списком, але елементи управління поки не зовсім зручні. Такий же конструктор airdrop для токенів EOS розробляється паралельно, оскільки має схожий інтерфейс і внутрішню логіку.
В цілому, тенденція до збільшення ваги клієнтського коду має місце в усіх розвинених рішеннях, і merkle tree - лише перший з цікавих алгоритмів. Інші схеми, такі як кільцеві підписи або zkSNARKs також вимагають серйозних обчислень на стороні браузера, тому компанія Smartz готується до реалізації великих і складних JS компонентів в конструкторах інтерфейсів на боці клієнта.
Висновок
Головна «перевага» традиційної розсилки токенів за списком в тому, що ця схема дозволяє закинути токени навіть тим, хто не бажає цього. Є й особливі збочення, коли токенів можна розіслати так мало, що біржа навіть не дозволяє робити з ними транзакції, і користувачі змушені спостерігати ці "шматки" на своїх адресах не маючи можливості їх позбутися.
Проблема airdrop, коли компанія роздає частину токенів системи в ком'юніті, звичайно вкрай важлива для розвитку проектів. Але таке рішення, на думку команди Smartz, недружелюбне до користувачів і неефективна в цілому. Та й взагалі смарт-контракти вкрай тяжіють до концепції «pull», а не «push», в якій саме користувачі мережі є ініціаторами і контролерами бізнес-процесів, а історії, коли хтось централізовано щось нав'язує десяткам тисяч користувачів, поступово відходять у минуле.
Обробка: Vinci