Узел Kaspa хранит одну каноническую истину: набор UTXO. Каждая монета, каждый расходуемый выход, каждый баланс — узел отслеживает всё это через консенсус GHOSTDAG и представляет как устоявшееся состояние. Но приложениям нужно больше, чем владение монетами. Маркетплейсу нужны жизненные циклы объявлений. Доске нужны цепочки постов и деревья ответов. Индексатору токенов нужны балансы держателей, полученные из переходов ковенантов. Ничего из этого не содержится в наборе UTXO.
Разрыв между «тем, что знает узел» и «тем, что нужно приложению» заполняют индексаторы — сервисы, которые следят за цепочкой, извлекают релевантные транзакции и проецируют их в пригодное для запросов состояние. На линейном блокчейне это просто: блоки приходят по порядку, подтверждения накапливаются, реорги редки. На BlockDAG Kaspa проблема структурно иная.
В этой статье объясняется, как работают реорг-осведомлённые индексаторы в Kaspa, почему важны перестраиваемые проекции и как продукты Kaspa Forge — Boards, Marketplace, платёжная инфраструктура и планируемый индексатор токенов — восполняют разрыв между канонической UTXO-истиной и состоянием приложения.
BlockDAG Kaspa: почему реорги — это структурная особенность, а не исключение
В традиционном блокчейне блоки образуют единую цепочку. Реорг происходит, когда более длинная цепочка заменяет текущую вершину — редкое, глубокое и разрушительное событие. Протокол GHOSTDAG в Kaspa порождает ориентированный ациклический граф, в котором несколько блоков могут майниться одновременно. Каждый блок имеет выбранного родителя, определяемого алгоритмом GHOSTDAG, а путь выбранных родителей формирует выбранную цепочку.
Ключевое свойство: выбранная цепочка может меняться. Виртуальный блок — псевдоблок на стороне узла, указывающий на все текущие вершины, — непрерывно переоценивает, какая вершина является выбранной. Когда выбранная вершина сдвигается, выбранная цепочка перемещается, а вместе с ней — канонический порядок принятых транзакций. В широком DAG такие небольшие реорги случаются часто. Они также неглубокие: гарантии безопасности GHOSTDAG означают, что цепочка быстро стабилизируется с небольшим суффиксом.
Это создаёт три проблемы для любого приложения, индексирующего Kaspa:
1. Транзакция может появляться в нескольких блоках DAG с разными значениями daa_score. Индексатор должен определить, какой скор является каноническим. 2. Ранее принятые транзакции могут выпасть из канонической цепочки. Индексатор должен обнаруживать это и отменять их эффекты. 3. Порядок должен быть детерминированным. Два индексатора, воспроизводящие одну и ту же историю, должны получить идентичное состояние независимо от порядка, в котором они наблюдали блоки.
Паттерн индексатора: событийная модель на DAG
Решение — событийная модель, адаптированная для BlockDAG. Вместо поддержания изменяемого состояния, отслеживающего вершину цепочки, индексатор рассматривает каждую релевантную транзакцию как неизменяемое событие и извлекает всё состояние приложения из воспроизводимого журнала событий.
Основной цикл:
loop:
fetch new blocks from the node (gRPC poll)
for each block:
extract relevant transactions
assign a canonical ordering key
record the event
recompute derived state from the event log
Сложность кроется в деталях. API получения блоков Kaspa возвращает блоки в том виде, в каком они появляются в DAG, но одна транзакция может быть включена в несколько блоков. Набор слияний цепочечного блока C включает все блоки в прошлом C, которых нет в прошлом выбранного родителя, — и транзакция, включённая в синий блок B, появится в наборе слияний того цепочечного блока, который первым принял B.
DAA score — скор алгоритма регулировки сложности равен синему скору плюс количество красных блоков, успешно объединённых и вознаграждённых на данный момент. Он контролирует график эмиссии и служит монотонно возрастающей меткой времени для упорядочивания событий в DAG.
Для индексатора практическое следствие состоит в том, что один и тот же txid может приходить с разными значениями daa_score в разных окнах опроса. Индексатор должен свести их в одну каноническую запись.
Детерминированная сортировка: проекция (daa, txid)
Индексаторы Kaspa Forge используют двухполевой ключ сортировки: (daa_score, txid). Поле daa_score обеспечивает временную упорядоченность — чем меньше, тем раньше, — а txid разрешает совпадения лексикографически. Эта пара является ключом проекции: детерминированными координатами, которые отображают событие цепочки в позицию в состоянии приложения.
Критический нюанс — схлопывание минимального daa. Когда одна и та же транзакция появляется в нескольких блоках DAG, индексатор берёт минимальное значение daa_score среди всех содержащих блоков:
// Conceptual — actual implementation is in the indexer's flatten_sorted
fn canonical_daa(txid: &TxId, observed: &[Block]) -> u64 {
observed.iter()
.filter(|b| b.contains(txid))
.map(|b| b.daa_score)
.min()
.expect("tx must appear in at least one block")
}
Это важно, потому что прямое сканирование работающего индексатора может увидеть транзакцию сначала в блоке с daa_score = 1_000_050, тогда как пересборка с нуля способна обнаружить её сначала при daa_score = 1_000_020. Без схлопывания минимального значения два запуска произведут разный порядок — лента будет дрейфовать между рабочим и пересобранным состоянием.
Когда более позднее окно опроса обнаруживает более низкий daa_score для уже проиндексированной транзакции, индексатор корректирует сохранённый daa вниз и пересортирует затронутую последовательность. Это гарантирует, что прямое сканирование сходится к тому же состоянию, которое произвела бы чистая пересборка.
Обработка реоргов: журналы отмен и атомарный откат
Реорг означает, что некоторые ранее принятые транзакции больше не являются каноническими. Индексатор должен обнаружить это и откатить свои проекции. Существует две стратегии, в зависимости от сложности приложения:
Пересборка с нуля. Для более простых проекций индексатор обнаруживает реорг, сравнивая текущую выбранную цепочку со своим сохранённым курсором. Когда цепочка сдвигается, он воспроизводит затронутый диапазон. Таблицы дедупликации предотвращают повторную обработку одного и того же конверта, а порядок (daa, txid) гарантирует, что воспроизведение даёт тот же результат.
Журнал отмен. Для сложного производного состояния — балансов токенов, списков держателей, обращающегося предложения — пересборка с нуля слишком дорога. Вместо этого индексатор записывает инверсию каждого перехода состояния. Когда реорг удаляет транзакцию, индексатор применяет записи отмен атомарно:
chain_cursor: { network, accepting_daa, accepting_hash, order }
undo_journal: { txid, inverse_delta, previous_state }
Курсор chain_cursor отслеживает, где индексатор считает себя в канонической цепочке. Когда узел сообщает, что ранее принятый блок больше не является каноническим, индексатор отходит назад по журналу отмен до последней допустимой позиции, применяет откаты, а затем воспроизводит вперёд от новой канонической цепочки.
Перестраиваемое состояние — состояние приложения, которое может быть полностью восстановлено путём воспроизведения истории канонической цепочки через логику проекции индексатора. Источником истины всегда является цепочка; база данных — это кеш.
Это фундаментальное отличие от изменяемого состояния: база данных никогда не является источником истины. Если она повреждена, утеряна или расходится с цепочкой, правильный ответ — пересобрать её, а не заплатывать.
Как продукты Kaspa Forge используют перестраиваемые проекции
Каждый продукт Kaspa Forge сталкивается со своей версией одной и той же задачи.
Boards: упорядочивание постов и дедупликация конвертов
Индексатор Boards опрашивает узел на предмет транзакций, содержащих конверты KBRD, — подписанные сообщения с текстом поста, ссылками на ответы и необязательными хешами изображений. Индексатор:
1. Разбирает и проверяет подпись BIP340 каждого конверта. Конверт самоаутентифицируется; несущая транзакция — лишь транспорт. 2. Назначает канонический порядок с использованием (daa, txid) и схлопывания минимального daa. 3. Дедуплицирует конверты — один и тот же подписанный конверт может быть передан повторно в другой транзакции. Таблица board_seen_envelopes (первичный ключ: хеш конверта) гарантирует, что первый txid, несущий данный конверт, является каноническим. 4. Строит деревья обсуждений — ссылки на ответы формируют DAG постов, проецируемый в плоский каталог с пагинацией.
Граница пересборки задокументирована честно: обрезанный узел способен воспроизвести лишь свой сохранённый горизонт. Для полной истории требуется архивный источник или актуальная резервная копия SQLite.
Маркетплейс: жизненный цикл объявлений на основе событий блокчейна
Маркетплейс не запускает отдельный индексатор блокчейна — состояние объявлений хранится в общей базе данных SQLite, управляемой наблюдателем эскроу. Но принцип тот же: переходы статуса объявлений (pending_moderation → published → reserved → closed) запускаются событиями блокчейна — пополнением эскроу, завершением сделки, истечением тайм-аута, — а база данных является проекцией этих событий.
Полнотекстовый индекс listings_fts пересобирается из теневых таблиц при запуске, если обнаружена несогласованность: практический пример перестраиваемого состояния. Производные поля, такие как available, вычисляются из фактической связи объявление↔сделка, а не из кешированных флагов.
Платежи: опрос UTXO и атрибуция адресов
Платёжная инфраструктура использует простейшую форму индексации цепочки: опрос getUtxosByAddresses через индекс UTXO узла. Каждый заказ получает уникальный адрес, производный от HD-дерева из xpub, поэтому атрибуция однозначна: платёж на адрес X равен заказу X. Наблюдатель суммирует все UTXO на адресе и сравнивает с ожидаемой суммой. Здесь нет сложной проекции для пересборки; набор UTXO *и есть* состояние приложения. Архитектура намеренно отдаёт предпочтение опросу, а не подпискам, для устойчивости к переподключениям.
Токены: полная событийная модель с журналом отмен
Планируемый индексатор токенов — ещё не запущен; контрактная работа ведётся как P1, профиль заморожен на P0, но развёртываемых артефактов пока нет — представляет наиболее сложный случай. Состояние токенов (семейства, ячейки, переходы, балансы держателей) извлекается из переходов ковенантов в BlockDAG. Индексатор должен:
- Отслеживать курсор цепочки с принимающим DAA, хешем и порядком.
- Поддерживать журнал отмен, достаточный для атомарного отката при реорге.
- Классифицировать каждый переход через версионный классификатор, проверяющий соответствие профилю
KF20-FIXED-v1. - Извлекать балансы и обращающееся предложение из канонических живых ячеек — никогда не храня их как первичное состояние.
Классификатор включает статус reorged: «ранее наблюдаемый переход выпал из канонической истории». Это не ошибка; это ожидаемое следствие работы на DAG. Индексатор обрабатывает это, применяя журнал отмен и реклассифицируя.
Каждый продукт Kaspa Forge — Boards, Marketplace, Escrow, Deposit — построен на одном принципе: BlockDAG Kaspa является источником истины, а базы данных приложений — перестраиваемые проекции. Ключи остаются на вашем устройстве в Desk; контракты и логика индексатора открыты. Если хостинговый сервис исчезнет, данные цепочки и правила проекции сохранятся. Подробнее об архитектуре каждого продукта — в обзоре архитектуры.
Компромиссы и честные границы
У этой архитектуры есть реальные издержки.
Обрезанные узлы ограничивают глубину пересборки. Стандартный узел Kaspa обрезает старые данные блоков. Если вам нужно пересобрать индексатор с нуля, обрезанный узел способен воспроизвести лишь свой сохранённый горизонт. Для полной истории требуется либо архивный узел, либо резервная копия базы данных. Индексатор Boards явно это документирует.
Глубокие реорги косметичны, но реальны. GHOSTDAG гарантирует неглубокие, быстро стабилизирующиеся реорги, но блок, навсегда оставшийся за границей прямого сканирования, не может быть пересмотрен без данных о принятии консенсусом. Для Boards это влияет только на порядок постов — не на целостность контента, поскольку подписанный конверт самоаутентифицируется независимо от того, какая транзакция его несла.
Схлопывание минимального daa имеет остаточный эффект. Рабочий индексатор может скорректировать daa только для уже увиденных транзакций. Блок, появившийся после того, как прямое сканирование прошло мимо его окна, вносит daa, который невозможно ретроактивно минимизировать. Это ограничено глубиной реорга и влияет на порядок, а не на корректность.
Производное состояние требует полного воспроизведения. Балансы токенов и данные об обращении нельзя инкрементально исправить после реорга — их нужно пересчитать из журнала отмен или с нуля. Это делает индексатор токенов более дорогим в эксплуатации по сравнению с более простыми проекциями, поэтому архитектура включает набор восстановления с открытым исходным кодом как поставляемый компонент до запуска основной сети.
Дедупликация конвертов — по конверту, а не по транзакции. Подпись KBRD привязывает содержимое конверта, а не несущую транзакцию. Байтово идентичный конверт, переданный повторно в другой транзакции, иначе был бы проиндексирован как новый пост. Защита от повторного воспроизведения предотвращает это, но означает, что первый txid, несущий данный конверт, является каноническим — выбор дизайна, который ставит целостность контента выше транспортной нейтральности.
Ни одна из этих проблем не является нерешённой. Это инженерные компромиссы, задокументированные и обработанные. Альтернатива — изменяемое состояние, предполагающее, что цепочка не будет реоргаться, — строго хуже на BlockDAG.
Продолжить изучение
архитектура транзакций Kaspa Forge
Следующий шаг: перейти к документации протоколов
FAQ
Что такое реорг в BlockDAG Kaspa?
Реорг происходит, когда выбранная вершина виртуального блока меняется, в результате чего выбранная цепочка — а значит, и канонический порядок принятых транзакций — сдвигается. В широком DAG Kaspa небольшие реорги случаются часто, но быстро стабилизируются благодаря гарантиям безопасности GHOSTDAG.
Почему приложения не могут просто читать набор UTXO?
Набор UTXO показывает, какие монеты существуют и кто может их потратить, но не содержит состояние уровня приложения — объявления на маркетплейсе, посты на доске или метаданные токенов. Это более богатое состояние должно быть получено путём индексации данных транзакций и их проецирования в пригодную для запросов форму.
Что происходит с состоянием приложения во время реорга?
Реорг-осведомлённый индексатор обнаруживает, когда ранее принятые транзакции выпадают из канонической цепочки, и откатывает затронутые проекции. Планируемый индексатор токенов Kaspa Forge, например, ведёт журнал отмен для атомарного отката.
Можно ли пересобрать индексатор с нуля?
Да — это и есть цель проектирования. Пересборка воспроизводит историю канонической цепочки из архивного узла или текущей резервной копии базы данных. Обрезанный узел способен воспроизвести лишь свой сохранённый горизонт, а не полную историю.
Чем это отличается от индексации линейного блокчейна?
В линейной цепочке блоки приходят в фиксированном порядке, а реорги редки. В BlockDAG Kaspa одна транзакция может появляться в нескольких блоках DAG с разными значениями DAA score, а выбранная цепочка способна сдвигаться. Индексатор должен свести всё это в одну детерминированную проекцию.
Делает ли высокая скорость создания блоков в Kaspa реорги более опасными?
Нет. Гарантии безопасности GHOSTDAG означают, что реорги неглубокие и быстро стабилизируются. Проблема носит архитектурный характер: индексатор должен корректно обрабатывать частые небольшие реорги, а не редкие глубокие.
Держите KAS там, где кражу можно отменить
Ковенант-сейф в мейннете Kaspa: ваши ключи, ваши правила, наш инструмент. Ончейн — бесплатно, навсегда.
Создать сейф
