Техническая архитектура Quantum Cats

Техническая архитектура Quantum Cats

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

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

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

Рисунки публикуются ончейн в свидетеле транзакции Taproot (в специальной кодировке, называемой «конвертом» – программное обеспечение анализирует транзакции, чтобы найти артефакты). Это означает, что любые данные записи неизменяемы и не могут быть изменены после публикации. Тем не менее, есть несколько способов, с помощью которых мы можем предоставить опыт изменения изображений, хотя на самом деле изображение никогда не меняется (и возможность иметь доступ к старой иллюстрации – это здорово, если она вам нравится больше!).

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

Когда одна запись ссылается на другую, это происходит по ее идентификатору. Идентификатор записи состоит из идентификатора транзакции Биткоина, в котором раскрываются данные записи, буквы i, а затем выходного индекса созданной записи. Например, запись 4b31771df21656d2a77e6fa18720a6dd94b04510b9065a7c67250d5c89ad2079i0 – это первая запись, созданная в биткоин-транзакции 4b31771df21656d2a77e6fa18720a6dd94b04510b9065a7c67250d5c89ad2079. Это означает, что если вы вписываете изображение (например, 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 кота. Эти 3333 записи относятся к идентификатору записи диспетчера, который относится к идентификаторам записей 40 слоев-соединителей, каждый из которых относится к одному или нескольким идентификаторам записей.

Идентификаторы записей включают идентификатор биткоин-транзакции. Идентификаторы биткоин-транзакций используются для их входов, выходов, версии и локтайма. Это означает, что если мы потратим UTXO для заранее подписанной транзакции на какую-то другую транзакцию, то мы никогда не сможем повторно создать тот же идентификатор транзакции и повредим нашу ссылку на запись! Чтобы избежать этого, мы создали UTXO для каждой заранее подписанной транзакции, а затем сохранили базу данных, чтобы отслеживать, какой UTXO был назначен для транзакции. Мы также автоматизировали проверки, чтобы убедиться, что никакие две записи не тратят один и тот же UTXO, что каждая транзакция с записью тратит только назначенный ей UTXO и что общий объем входов и выходов всех транзакций (включая комиссии) соответствует ожиданиям. Эти проверки выполнялись всякий раз, когда система взаимодействовала с кошельками или ключами, и давали нам уверенность в том, что подписано лишь то, что должно быть подписано. Кроме того, мы использовали отдельные кошельки для разных типов записи активов, чтобы добавить дополнительную защиту от ошибки, приводящей к двойному назначению UTXO. Мы также создали тестовую программу, которая проверяла все назначения и публикации записей в regtest, а затем проверяла, соответствуют ли данные, попавшие в цепочку, тем, что были в нашей базе данных уровня управления.

Такое предварительное подписание транзакций означало, что нам приходилось заранее брать на себя комиссию за каждую запись. Мы не можем знать, какими будут комиссии в конечном итоге, поэтому мы решили назначить для транзакций разумную ставку комиссии, а затем создать инструменты для повышения комиссий в будущем, если мы назначим слишком низкую комиссию (если мы назначили комиссию выше, чем необходимо, просто придется с этим смириться, поэтому частью анализа был выбор ставки комиссии, которая нас устроит, даже если окажется, что она выше). Помимо использования услуги ускорителя транзакций (внешняя плата майнеру за включение транзакции в блок, даже если он платит комиссию ниже рыночной), существует два метода увеличения эффективной ставки комиссии за транзакцию: Replace-By-Fee (RBF) и Child-Pays-For-Parent (CPFP). RBF предполагает повторное расходование входных данных транзакции в новой транзакции, за которую взимается более высокая комиссия. Поскольку наше приложение использует идентификаторы предварительно зафиксированных транзакций, такой вариант невозможен. CPFP предполагает использование неподтвержденных результатов транзакции в новой транзакции, которая платит более высокую комиссию, чем «родительская». Чтобы майнеры могли получать комиссию от «дочерней» транзакции, они должны включить в пакет как родительскую, так и дочернюю транзакцию. Эффективная ставка комиссии в конечном итоге представляет собой общую сумму уплаченных комиссий, разделенную на общий виртуальный размер пакета (все транзакции вместе). Поскольку родительская транзакция не нарушена, это был именно тот механизм взимания комиссий, который нам был нужен.

Оставалась одна проблема: у нас потенциально были сотни транзакций, за которые нужно было бы взимать комиссию. В дополнение к сложности точного обнаружения 10 или 100 неподтвержденных транзакций вручную, существуют также политики ретрансляции, которые предотвращают передачу через сеть пакета размером более 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.

Почему Биткоин – это «заморозка», в которой отчаянно нуждаются ваши сбережения Почему Биткоин – это «заморозка», в которой отчаянно нуждаются ваши сбережения По мере развития технологического прогресса свободный рынок неумолимо движется к разбавлению средств. Биткоин – это глубокая «заморозка», в которой отчаянно нуждаются ваши сбережения. Unchained Capital 12 мая 2024
Настройка мультиподписи своими руками или совместное хранение с мультиподписью? Настройка мультиподписи своими руками или совместное хранение с мультиподписью? Решение перевести биткоин на самостоятельное хранение – это только первый шаг. Держатели должны решить, как они хотят защитить свои сбережения: с помощью единой подписи, самостоятельно созданной мультиподписи или совместного хранения. Unchained Capital 12 мая 2024
Отказ от банков: Биткоин предлагает абсолютную финансовую свободу Отказ от банков: Биткоин предлагает абсолютную финансовую свободу Абсолютный дефицит – не единственная ценность Биткоина. Предоставление пользователям возможности в одностороннем порядке контролировать свою финансовую жизнь бесценно. Bitcoin Magazine 11 мая 2024