Технічна архітектура Quantum Cats

Технічна архітектура Quantum Cats

Rijndael, технічний директор Taproot Wizards, про технічну архітектуру, розроблену для артефактів Quantum Cats.

Quantum Cats – це колекція із 3333 записів Ordinals. Це перша колекція записів, яка буде змінюватися з часом, і була створена в час високих комісій та їхнього непередбачуваного майбутнього. Ця стаття не про естетичні переваги NFT (на мою думку, вони виглядають круто) або причини їхнього існування на ринку; це стаття про технічну реалізацію Quantum Cats. Я вважаю, що інженерні проблеми, з якими ми зіткнулися, та методи, які ми застосували для розв'язання цих проблем, цікаві та потенційно корисні як для майбутніх творців Ordinals, так і для інших розробників Біткоїн-застосунків загалом.

Перш ніж заглиблюватися в технічні тонкощі Quantum Cats, важливо зрозуміти, що ми хотіли створити. Користувачі Ordinals зберігають записи (цифрові предмети колекціонування або артефакти, які реалізовані в протоколі Ordinals і передаються разом з біткоїн-транзакціями) в біткоїн-гаманцях із самостійним зберіганням, які мають функції управління монетами та створення транзакцій, що дозволяють передавати певні записи, а також підписувати складні типи транзакцій (наприклад, пропозиції, які не потребують довіри, і свопи на маркетплейсах для артефактів). Ми хотіли створити колекцію артефактів, яка буде видозмінюватись з часом, додаючи або змінюючи атрибути або риси котів.

Ілюстрації публікуються ончейн у свідку транзакції Taproot (за допомогою спеціального кодування, яке називається Envelope («конвертом») – програмне забезпечення аналізує транзакції, щоб знайти артефакти). Це означає, що будь-які дані запису незмінні та не можуть бути змінені після публікації. Проте є кілька способів, за допомогою яких ми можемо надати досвід зміни зображень, хоча насправді зображення ніколи не змінюється (і можливість мати доступ до старої ілюстрації – це чудово, якщо вона вам подобається більше!).

Рекурсія – це особливість артефактів, коли один запис може посилатися на вміст іншого. Наприклад, ви можете вписати HTML-сторінку та включити до неї зображення, які є в інших записах. Програмне забезпечення Ordinals відтворює HTML-сторінки в iframe, тому вміст артефакту на стороні клієнта може складатися з кількох записів. HTML-записи не можуть містити контент з ширшої мережі, а лише з інших записів або невеликого набору інших кінцевих точок, наданих програмним забезпеченням (наприклад, існує кінцева точка для отримання поточної висоти Біткоїн-блоку). Це означає, що всі рекурсивні записи все ще знаходяться в ланцюжку і можуть розпадатися, що забезпечує можливість компонування і повторне використання загальних компонентів. Наприклад, всі «квантові коти» з червоним тлом можуть посилатися на один запис із червоним тлом, замість того, щоб поміщати одні й ті ж дані в ланцюжок для них усіх.

Коли один запис посилається на інший, це відбувається за його ідентифікатором. Ідентифікатор запису складається з ідентифікатора транзакції Біткоїна, в якому розкриваються дані запису, літери та індекс виходу створеного запису. Наприклад, запис 4b31771df21656d2a77e6fa18720a6dd94b04510b9065a7c67250d5c89ad2079i0 – це перший запис, створений у біткоїн-транзакції 4b3177d 4510b9065a7c67250d5c89ad2079.

Це означає, що якщо ви вписуєте зображення (наприклад, png), а потім вписуєте HTML-сторінку, яка включає ідентифікатор запису зображення, в тег img, HTML-запис відображатиме вміст запису зображення. Якщо HTML-запис посилається на зображення-запис, яке фактично не знаходиться ончейн (поки що), то сервер покаже помилку 404 (не знайдено), яку HTML-запис може непомітно проковтнути. Якщо попередньо підписати зображення, але не транслювати їх у мережу Біткоїна, можна отримати їхні майбутні ідентифікатори (оскільки вони є лише ідентифікатором транзакції та індексом) і включити ці ідентифікатори записів у HTML-записи, які ми транслюємо. Якщо хтось перегляне HTML-запис, він може відображати вміст своїх посилань, що знаходяться в ланцюжку, але не зможе відобразити заздалегідь підписані компоненти, що не транслюються. У міру опублікування більшої кількості компонентів HTML-запис зможе автоматично відображати їх.

Це основний механізм, який колекція Quantum Cats використовує для зміни своїх творів мистецтва – заздалегідь підписані транзакції, які дозволяють поступово розкривати нові характеристики з часом. Як ми побачимо, управління комісіями та динаміка ринку створили труднощі, через які «квантовим котам» були потрібні деякі додаткові рівні опосередкованості та функцій, але заздалегідь підписані транзакції з заздалегідь обчисленими ідентифікаторами транзакцій є ключовою особливістю Біткоїна, яка зробила це можливим.

Хоча вміст заздалегідь підписаного, але нерозкритого запису невідомий до того, як транзакція транслюватиметься, той самий ідентифікатор запису матиме той самий вміст. Це створило проблему: навіть якщо майбутня риса кота буде невідома, користувачі зможуть підрахувати, скільки разів виникав конкретний ідентифікатор запису, і зможуть визначити, які майбутні риси будуть більш рідкісними, і зможуть обмінюватися котами на їхню майбутню еволюцію.

Ми хотіли, щоб еволюція була несподіваною та веселою, адже дуже цікаво не знати заздалегідь, як майбутня еволюція вплине на відносну рідкість кішок. Тому ми запровадили рівень опосередкованості: кожен кіт посилається на призначений (але нерозкритий) «з'єднувач шарів», який дозволяє відображати кота за унікальним ідентифікатором на заздалегідь призначене зображення. Це означає, наприклад, що кожен кіт посилається на той самий з'єднувач шарів для свого вихідного фонового зображення. Тільки після того, як цей з'єднувач шарів буде трансльований в мережу, користувачі зможуть дізнатися, який фон більш чи менш поширений.

Цей метод також дозволив заощадити простір: оскільки кожен кіт посилається на ідентичні з'єднувачі, HTML-код для кота для імпорту з'єднувачів шарів можна вписати один раз, а потім посилатися на нього кожному з 3333 записів. Фактично, кожен запис котів було зменшено до 109 байтів: лише унікальний ідентифікатор кота та тег скрипту для імпорту логіки для отримання та візуалізації загального набору з'єднувачів шарів, пошуку унікального зображення для кожного шару та рендерингу. Можливість перемістити відображення кожного кота з окремих записів в загальний запис не тільки усунуло проблему витоку інформації про відносну рідкість характеристик, а й заощадило приблизно 5 BTC!

Завдяки введенню з'єднувачів шарів та об'єднанню логіки рендерингу в загальний компонент тепер вписується 4 типи даних:

  • Зображення для кожної характеристики кішки (фонове зображення, тіло чи очі).
  • З'єднувач шарів, який відображає кота за ідентифікатором на конкретний артефакт. Таке відображення відбувається один раз для кожного шару (фон, тіло, очі, рот і т. д.).
  • Основна логіка диспетчеризації та рендерингу. Ми називаємо це «Диспетчер». Він відповідає за вибірку з'єднувача шарів, пошук зображення кішки у з'єднувачі, отримання цього графічного об'єкта та його рендеринг. Саме цей послідовний рендеринг є причиною, чому ми моделюємо артефакти шарами.
  • Окремий кіт, який передається колекціонерові. Він займає 109 байтів і включає унікальний ідентифікатор і посилання на диспетчер, в якому знаходиться весь код рендерингу.

У Quantum Cats є кілька сотень графічних ресурсів, 40 шарів (тобто 40 шарів-конекторів), 1 диспетчер та 3333 коти. Ці записи посилаються на ідентифікатор запису диспетчера, який посилається на ідентифікатори записів 40 шарів-з'єднувачів, кожен з яких посилається на один або декілька ідентифікаторів записів.

Ідентифікатори записів містять ідентифікатор біткоїн-транзакції. Ідентифікатори біткоїн-транзакцій використовуються для їхніх входів, виходів, версії та локтайму. Це означає, що якщо ми витратимо UTXO для попередньо підписаної транзакції на якусь іншу транзакцію, то ми ніколи не зможемо повторно створити той самий ідентифікатор транзакції та пошкодимо наше посилання на запис! Щоб уникнути цього, ми створили UTXO для кожної попередньо підписаної транзакції та підтримуємо базу даних, щоб відстежувати, який UTXO був призначений для транзакції.

Ми також автоматизували перевірки, щоб переконатися, що жодні два записи не витрачають один і той же UTXO, що кожна транзакція із записом витрачає тільки призначений їй UTXO і що загальний обсяг входів та виходів усіх транзакцій (включно з комісією) відповідає очікуванням. Ці перевірки виконувались щоразу, коли система взаємодіяла з гаманцями чи ключами, і давали нам упевненість у тому, що підписано лише те, що має бути підписано.

Крім того, ми використовували окремі гаманці для різних типів записів, щоб додати додатковий захист від помилки, що призводить до подвійного призначення UTXO. Ми також створили тестову програму, яка перевіряла всі призначення та публікації записів у regtest та чи відповідають дані, які потрапили в ланцюжок, тим, що містяться в нашій базі даних рівня управління.

Таке попереднє підписання транзакцій означало, що нам доводилося заздалегідь брати на себе комісію за кожний запис. Ми не можемо знати, якими будуть комісії в майбутньому, тому ми вирішили призначити розумну ставку комісії, а потім створити інструменти для підвищення комісій у майбутньому, якщо ми призначимо занизьку комісію (якщо ми призначили комісію вище, ніж потрібно, просто доведеться з цим змиритися, тому частиною аналізу був вибір ставки комісії, яка нас влаштує, навіть якщо виявиться, що вона зависока).

Крім використання послуги прискорення транзакцій (зовнішня плата майнеру за включення транзакції до блоку, навіть якщо він сплачує комісію нижче за ринкову), існує два методи збільшення ефективної ставки комісії за транзакцію: Replace-By-Fee (RBF) та Child-Pays-For-Parent (CPFP). RBF передбачає повторне витрачання вхідних даних транзакції у новій транзакції, за яку стягується вища комісія. Оскільки наша програма використовує ідентифікатори попередньо зафіксованих транзакцій, такий варіант неможливий.

CPFP передбачає використання непідтверджених результатів транзакції у новій транзакції, яка сплачує вищу комісію, ніж «батьківська». Щоб майнери могли отримувати комісію від «дочірньої» транзакції, вони повинні включити як батьківську, так і дочірню транзакцію. Ефективна ставка комісії зрештою дорівнює загальній сплаченій комісії, поділеній на загальний віртуальний розмір пакета (усі транзакції разом). Оскільки батьківська транзакція не порушена, це був саме той механізм стягування комісій, який нам потрібен.

Залишалася одна проблема: у нас потенційно були сотні транзакцій, які потребували б підвищення комісії. На додаток до складності виконання цього вручну, існують також політики ретрансляції, які запобігають передачі через мережу пакета розміром понад 101 KvB (віртуальних кілобайтів) або понад 25 транзакцій. Це означає, що якби нам потрібно було виконати 50 транзакцій CPFP, їх краще було б виконувати паралельно, а не послідовно. Для цього ми створили інструмент, який:

  • Переглядає список непідтверджених транзакцій та для кожної з них обчислює вартість CPFP-доведення цих транзакцій до цільової ставки комісії.
  • Агрегує ці суми як вихідні дані у новій транзакції, яка була витрачена з одного входу на всі UTXO, необхідні для паралельного збільшення комісій цільових транзакцій.
  • Пропонує оператору відправити необхідну кількість біткоїнів (також розраховує комісію за транзакцію поділу) на одну адресу.
  • Після отримання депозиту, транслює транзакцію, щоб поділити депозит на один UTXO для кожної транзакції.
  • Створює і транслює транзакції CPFP для транзакцій, що зависли.

Ми протестували цю систему в Regtest, виконуючи до 300 транзакцій одночасно. Ви можете побачити «розділену» транзакцію тут: https://mempool.space/tx/2ec4a8708524faf9901c69da8518b632ec31762730218d3b38ff40954cee882f Кожен із цих виходів фінансує CPFP для збільшення транзакції розкриття запису з 65 до 150 сатоші/vb.

Арт-активи становили ~90% від загального обсягу даних проєкту. Ми хотіли опублікувати всі або більшу частину зображень, поки комісії були низькими. Але ми також не хотіли, щоб люди побачили ці зображення, перш ніж кішки будуть готові еволюціонувати. Отже, ми вирішили зашифрувати зображення, а потім опублікувати ключ дешифрування зображення за допомогою з'єднувача шарів (який містить відображення, необхідне Коту для отримання своєї характеристики).

Це дозволило нам відокремити етап публікації даних від виявлення характеристики. Це дозволило нам скористатися періодом нижчих комісій для масової публікації даних, зберігаючи при цьому можливість показати світові артефакти у потрібний час. Механізм тут простий: перед призначенням ресурсів графічного зображення всі зображення певного шару (знову, наприклад, фону, очей чи рота) шифруються з допомогою ключа шифрування кожного шару.

Це зашифроване зображення використовується в заданому записі як потік байтів. Потім ключ шифрування відображається у з'єднувачі шарів (який знову заздалегідь призначений). Коли диспетчер отримує з'єднувач шару, він зчитує зіставлення Cat-ID -> художній актив, а також ключ дешифрування для цього шару. Він отримує артресурс у вигляді масиву байтів, а потім використовує бібліотеки шифрування браузера для розшифровки зображення у форматі PNG, і, зрештою, переносить його на полотно.

Таким чином, кожен Quantum Cat – це невеликий запис, який отримує загальний запис, що містить код відправки, розшифровки та рендерингу. Цей код отримує стільки з'єднувачів шарів, скільки доступно в ланцюжку (не завжди, тому що вони можуть бути попередньо підписані, але не транслюються). Потім він використовує ідентифікатори записів і ключі розшифровки цих з'єднувачів шарів для отримання зашифрованих зображень в інших записах, розшифровує їх, а потім відтворює їх на полотні.

Коли нам потрібно транслювати ці підписані записи, ми використовуємо масові паралельні транзакції CPFP, щоб підвищити їх до правильної ставки комісії без необхідності заздалегідь брати на себе надто високу комісію. Кінцевим результатом всього цього є те, що користувачі мають у своєму гаманці Quantum Cat, який з часом отримує нові риси та атрибути, при цьому всі його зображення залишаються незмінними у Біткоїні.

Є й інші аспекти проєкту, які ми тут не розглянули: як код браузера справляється з періодичними збоями під час отримання всіх цих ресурсів, як користувачу керувати колекцією, що розвивається, як ми керували процесом створення UTXO для всіх попередньо підписаних ресурсів (це легко: це той же код розділення UTXO, описаний вище для UTXO CPFP). Але я сподіваюся, що наведена вище інформація здасться вам цікавою та корисною як для проєктів записів, так і в іншому проєкті із заздалегідь підписаними транзакціями.

Це гостьовий пост Rijndael. Висловлені погляди є його власними і не обов’язково збігаються з точкою зору BTC Inc. або Bitcoin Magazine.

Як правильно поводитися з токенами BRC-20 та Ordinals Як правильно поводитися з токенами BRC-20 та Ordinals Прагматичний погляд на проблему Ordinals і токенів у Біткоїні та на те, як розв'язати проблему їхнього використання блокового простору. Роббі Грінфілд 19 травня 2024
Чому Біткоїн – це вкрай потрібне «замороження» для ваших заощаджень Чому Біткоїн – це вкрай потрібне «замороження» для ваших заощаджень З розвитком технологічного прогресу вільний ринок невблаганно рухається до «розбавлення» коштів. Біткоїн – це глибоке «замороження», якого відчайдушно потребують ваші заощадження. Unchained Capital 12 травня 2024
Налаштування мультипідпису власноруч чи спільне зберігання з мультипідписом? Налаштування мультипідпису власноруч чи спільне зберігання з мультипідписом? Рішення перевести біткоїн на самостійне зберігання – це лише перший крок. Власники повинні також вирішити, як вони хочуть захистити свої заощадження: за допомогою єдиного підпису, самостійно створеного мультипідпису чи спільного зберігання. Unchained Capital 12 травня 2024