Kaspa Forge
Разбор

Семь ковенантных путей в конечном автомате Kaspa Dice

30 августа 2026 Автор — ИИ-команда OfficeForge · проверено командой 9 мин чтения
Ковенант Kaspa Dice: семь путей расходования в блокчейне

Честная ончейн-игра в кости должна решить задачу, которая звучит просто, но на деле таковой не является: две стороны, не доверяющие друг другу, должны совместно сгенерировать случайный результат, зафиксировать свои входные данные до того, как увидят чужие, и произвести расчёт выплаты — всё это без передачи хранения третьей стороне. Ковенант костей Kaspa решает эту задачу с помощью семи путей расходования, организованных в конечный автомат (FSM), охватывающий три ончейн-транзакции. Каждый путь — это ветвь ковенантного скрипта, условие, при котором UTXO может быть потрачен, — и вместе они обеспечивают полный жизненный цикл одной ставки.

В этой статье описаны все семь путей, объяснены криптографические обязательства, обеспечивающие честность игры, и показано, как структура ставок комиссии делает честное поведение доминирующим в мемпуле. Arena Dice находится в разработке; постоянная продакшн-ветка отключена до завершения ревью. Далее речь идёт о протокольной инженерии, а не об анонсе продукта.

Проблема: обязательство без видимости

При подбрасывании монеты между двумя незнакомцами каждая сторона обладает секретным случайным сидом. Честный протокол — это коммит-раскрытие: обе стороны фиксируют данные, публикуя хеши, затем обе раскрывают их, и результат вычисляется на основе двух раскрытых значений. Если бы любая сторона могла увидеть сид другой до фиксации, она могла бы участвовать только тогда, когда выигрывает.

В Kaspa «обязательство» — это UTXO, заблокированный ковенантным скриптом, который проверяет хеш BLAKE3 против значения, предоставленного при расходовании. Вики Kaspa описывает модель транзакций и UTXO, делающую это возможным. Ковенант костей расширяет базовую блокировку хешированием с разделением доменов: каждый хеш включает версионированную доменную строку, поэтому обязательство на сид никогда не может совпасть с обязательством на идентификатор комнаты или результат броска.

Определение

Ковенант — условие расходования UTXO, которое проверяет данные транзакции расходования, а не только подписи. В Kaspa ковенанты активируются хардфорком Toccata и используют опкоды вроде OpBlake3 для проверки хешей против зафиксированных значений непосредственно в скрипте.

Семь ветвей

FSM имеет две UTXO-фазы — Комната и Игра — с двумя состояниями Игры. Их покрывают два шаблона скриптов: один для Комнаты, один для Игры. Шаблон Игры содержит оба состояния T0 и T1; когда PLAYER_REVEAL расходует монету T0, он пересобирает наследника как тот же скрипт с новой областью данных в начале (раскрытый сид). Семь ветвей связывают состояния:

Room
├── DICE_JOIN        player sig     → Game T0  (carries player_commit)
└── ROOM_TIMEOUT     permissionless → house

Game · T0_AWAITING_PLAYER_REVEAL
├── PLAYER_REVEAL           player sig     → Game T1  (seed in state)
└── PLAYER_REVEAL_TIMEOUT   permissionless → stake to player, room_value to house

Game · T1_AWAITING_HOUSE_REVEAL
├── HOUSE_REVEAL_WIN        house sig      roll < T  → player: win_payout
├── HOUSE_REVEAL_LOSE       house sig      roll ≥ T  → house: game_value − fee
└── HOUSE_REVEAL_TIMEOUT    permissionless → player: entire Game UTXO

«Без разрешения» означает, что любой может отправить транзакцию — подпись ни одной из сторон не требуется. Эти ветви тайм-аутов существуют, чтобы средства не оказались заблокированы навсегда, если одна из сторон исчезнет.

Никаких ZK-доказательств, деревьев Меркла, доказателей или залогов не задействовано. Три транзакции на ставку, две подписи от игрока, одна от дома.

Жизненный цикл из трёх транзакций

Транзакция 1 — JOIN. Игрок подписывает транзакцию с ковенантной защитой, которая блокирует его ставку в Game UTXO в состоянии T0. Транзакция несёт player_commit — хеш BLAKE3 32-байтового сида игрока:

player_commit = BLAKE3(KASPAFORGE_DICE_PLAYER_SEED_V1
                       ‖ game_id ‖ player_seed)

Сам сид не попадает в блокчейн. Обязательство дома (house_commit) было уже зафиксировано в байтах Комнаты до того, как игрок сел за стол. На этом этапе ни одна сторона не может вычислить результат броска — BLAKE3 является PRF, и каждое обязательство скрывает соответствующий сид.

Транзакция 2 — PLAYER_REVEAL. Клиент игрока автоматически подписывает и отправляет эту транзакцию после подтверждения JOIN. Раскрытый player_seed записывается в состояние Игры. Этот шаг автоматизирован по замыслу: у игрока нет осмысленного выбора (он не знает результата), а необходимость ручного клика превратила бы игру в один клик в игру в два клика с таймером между ними.

Если браузер игрока закроется до раскрытия, сид будет утерян — это сделано намеренно, а не по недосмотру. Восстанавливаемый сид — это сид, который дом тоже мог бы получить. После дедлайна player_reveal_daa любой может отправить PLAYER_REVEAL_TIMEOUT, вернув игроку точную сумму ставки и передав стоимость комнаты дому. Игрок потеряет только комиссию за исходную транзакцию JOIN.

Транзакция 3 — HOUSE_REVEAL. Дом раскрывает house_seed. Ковенант вычисляет результат броска в блокчейне:

roll_u16 = LE_u16( BLAKE3(KASPAFORGE_DICE_ROLL_V1
                          ‖ game_id ‖ house_seed ‖ player_seed)[0..2] )
win ⟺ roll_u16 < win_threshold

Если результат ниже порога, ковенант переходит к HOUSE_REVEAL_WIN и выплачивает игроку win_payout. В противном случае HOUSE_REVEAL_LOSE выплачивает дому game_value − fee. Если дом пропускает свой дедлайн, HOUSE_REVEAL_TIMEOUT (без разрешения) возвращает весь Game UTXO игроку.

Криптографический механизм

Разделение доменов. Шесть версионированных доменных строк гарантируют, что хеши, построенные из одних и тех же входных данных для разных целей, никогда не совпадут:

  • KASPAFORGE_DICE_ROOM_V1 — идентификатор комнаты и домен защиты от повторного использования
  • KASPAFORGE_DICE_HOUSE_SEED_V1 — обязательство дома
  • KASPAFORGE_DICE_PLAYER_SEED_V1 — обязательство игрока (добавлено в V2)
  • KASPAFORGE_DICE_ROLL_V1 — вычисление броска
  • KASPAFORGE_DICE_RULES_V1 — условия ставки в виде одного хеша
  • KASPAFORGE_DICE_STATE_V1 — дайджест компоновки области состояния

Тест проверяет попарную различность со всеми доменами Blackjack, включая проверку коллизий префиксов — поскольку эти строки конкатенируются, а не разделяются по длине, одна доменная строка, являющаяся префиксом другой, означала бы реальную коллизию.

Идентификатор игры. game_id выводится из якорной транзакции Комнаты, не позволяя двум комнатам разделять идентичность и допуская повторное использование обязательств между столами.

Диапазон — степень двойки. Бросок читает два байта дайджеста BLAKE3, давая равномерный диапазон 0–65535. Это сделано намеренно: 2^16 делит хеш-пространство нацело, поэтому ни одно значение не встречается чаще других. Использование mod 10000 внесло бы смещение 16,67% на малых остатках (поскольку 65536 = 6 × 10000 + 5536, остатки ниже 5536 встречаются семь раз за период, а остальные — шесть). Измеренная равномерность проходит χ²-тесты на 10⁷ бросках по двум независимым лентам сидов.

Обработка знакового бита. Kaspa читает элементы стека как знаковые числа в формате little-endian. Необработанный двухбайтовый фрагмент со старшим байтом ≥ 0x80 прочитался бы как отрицательное число, что сделало бы половину всех бросков выигрышными при любом положительном пороге. Ковенант добавляет нулевой байт (substr(0,2) ‖ 0x00) для получения трёхбайтового неотрицательного целого числа. При включённом covenants_enabled узел не требует минимальной кодировки, поэтому избыточный третий байт принимается.

Генерация сида. Сид игрока создаётся клиентским крейтом (DicePlayerSecret::mint()) с использованием CSPRNG браузера. У типа нет Clone или Debug — это обеспечивается системой типов, а не анализом исходного кода, — потому что копируемый сид — это сид, который может утечь. Конструктор from_entropy существует для кошельков с аппаратными источниками ГСЧ, но качество энтропии лежит на вызывающем коде. В коде прямо указано ограничение: *он не может сертифицировать энтропию, и ничто не может*.

Доминирование по ставке комиссии: почему честные действия побеждают

Мемпул Kaspa выбирает транзакции по ставке комиссии — комиссия за грамм массы, а не просто комиссия. Каждое честное действие (раскрытие, расчёт) конкурирует с альтернативой без разрешения (тайм-аут) на том же UTXO. Если бы тайм-аут имел более высокую ставку комиссии, он выиграл бы аукцион замены, и честная транзакция никогда не подтвердилась бы.

Дизайн V3 обеспечивает инвариант доминирования по ставке комиссии: для каждой пары «честное действие vs. конкурент без разрешения на той же монете» честное действие имеет строго более высокую ставку комиссии. Измерено на эталонном профиле при ставке 2×:

ПараСтавка честного действияКонкурентЗапас
PLAYER_REVEAL vs PLAYER_REVEAL_TIMEOUT573,7481,61,19×
HOUSE_REVEAL_WIN vs HOUSE_REVEAL_TIMEOUT2 840,9621,54,57×
HOUSE_REVEAL_LOSE vs HOUSE_REVEAL_TIMEOUT2 927,4621,54,71×

Наименьший запас во всём контуре — 3,21× (PLAYER_REVEAL_TIMEOUT при более высоких ставках). Предыдущий дизайн сравнивал *суммы* комиссий, но мемпул сравнивает *ставки* комиссий. Ветвь с пятикратно большим лимитом комиссии всё равно может проиграть аукцион, если она весит в восемь раз больше. V3 измеряет ставки, а не суммы, и генератор отклоняет любую комнату, нарушающую инвариант.

Измеренные затраты

При базовой сетевой ставке комиссии 100 сомпи/грамм полная стоимость ставки составляет:

Win path:   JOIN + PLAYER_REVEAL + WIN  = 11 656 g  → 0.011656 KAS
Lose path:  JOIN + PLAYER_REVEAL + LOSE = 11 552 g  → 0.011552 KAS
Expected at 2× (p = 0.489990):                          0.011603 KAS

Это примерно вдвое дешевле V2. Экономия достигнута за счёт увеличения резерва дома (0,5 → 3 KAS), что сократило массу хранения ветви выигрышного расчёта с 24 267 до 1 831 граммов. Дом оплачивает это замороженным капиталом, а не игрок.

Лестница определяет предустановленные пары множитель/порог при преимуществе дома 2%:

МножительПорогВероятность выигрышаРеализованное преимущество
32 1120,4899902,0019%
12 8450,1959992,0004%
10×6 4220,0979922,0080%

Формула порога использует деление с округлением вниз, что слегка смещает реализованную маржу в пользу дома — с ограничением MAX_EDGE_OVERSHOOT_PPM = 500.

Как это используется в Kaspa Forge

Ковенантный FSM костей является частью семейства протоколов Arena на Kaspa Forge — той же инфраструктуры, на которой работает Arena Blackjack, публичная карточная игра на бете основной сети. Продукты Arena следуют общему принципу: средства находятся в ончейн-UTXO Комнаты/Игры, игроки подписывают локально через Desk, а расчёт обеспечивается ковенантными скриптами, а не балансами платформы. Принцип некастодиального хранения распространяется на всё семейство — Kaspa Safe использует ковенантные хранилища с выводами по тайм-локу, Escrow блокирует средства для P2P-сделок, Deposit удерживает залог в блокчейне.

Для Dice конкретно уровень консенсуса (V3) прошёл три независимых раунда проверки. Фазы контроллера P1 (построители расчётов дома) и P2 (демон с сидами для каждой ставки) построены и протестированы на мейннете с собственными кошельками команды — шесть комнат, пять ставок, шесть из семи путей проверены. Фаза P3 (комнаты, интерфейс, политика оператора) и боевое развёртывание не существуют.

Arena Blackjack — действующий продукт Arena на мейннете Kaspa, верифицируемая карточная игра, в которой средства хранятся в ончейн-UTXO Комнаты/Игры, а доказательства можно проверить независимо. Arena Dice использует ту же ковенантную инфраструктуру и основы протокола Arena, но его постоянная продакшн-ветка остаётся отключённой до завершения ревью. Следите за инженерными обновлениями в блоге Kaspa Forge или исследуйте действующую игру через Desk.

Создать сейф

Компромиссы и текущие ограничения

Три транзакции на ставку. Это дороже, чем дизайн с двумя транзакциями. Третья транзакция существует, потому что исходная двухтранзакционная версия (V1) содержала критический недостаток: сид игрока передавался в JOIN в открытом виде, позволяя дому вычислить результат до подтверждения — и избирательно отменять невыгодные ставки. Разделение на коммит и раскрытие — это исправление, и оно стоит ещё одного ончейн-раунда.

Игрок должен оставаться в сети для автоматического раскрытия. Клиент повторно отправляет подписанную PLAYER_REVEAL до дедлайна. Если вкладка закрывается, сид утрачен и активируется путь тайм-аута. Это намеренное решение: восстанавливаемый сид — это скомпрометированный сид. Окно тайм-аута устанавливается коротким, чтобы закрытая вкладка не блокировала капитал дома надолго.

Поздний JOIN после дедлайна комнаты. В Kaspa нет опкода «не старее чем» — только «не моложе чем». JOIN, поступивший после дедлайна комнаты, конкурирует с ROOM_TIMEOUT на том же UTXO. Дом не может избирательно отменять (на этом этапе он не может отличить выигрышные ставки от проигрышных), но операционная мера в том, что лобби не должно продавать места после дедлайна, а дом должен своевременно возвращать просроченные комнаты.

Заморозка капитала. Дом резервирует (multiplier − 1) × stake в каждой комнате на весь срок. При 10× и ставке 50 KAS это 450 KAS заблокировано на столе. Это цена того, чтобы ветвь выигрышного расчёта была достаточно лёгкой для доминирования над конкурентом-тайм-аутом в аукционе мемпула.

Не боевое, не публичное. Постоянная продакшн-ветка отключена до завершения ревью. Нет интерфейса, нет публичных столов, ARENA_DICE_ARMED=0. Код консенсуса, контроллер и криптографические домены построены и проверены — но граница продукта чёткая: это инженерия, а не сервис.

Маршрут по теме

Продолжить изучение

протокол Arena и руководство по проверке

Связанные материалы

Следующий шаг: посмотреть действующие столы Arena

FAQ

Что такое ковенант костей Kaspa?

Набор ончейн-ковенантных скриптов, обеспечивающих честную игру в кости между игроком и домом без необходимости доверять друг другу или третьей стороне. Семь путей расходования в трёх транзакциях, с обязательствами на BLAKE3, гарантирующими, что ни одна сторона не может предсказать результат до фиксации.

Почему для игры в кости нужны три ончейн-транзакции?

Два секретных сида требуют двух транзакций раскрытия плюс начальный JOIN. JOIN блокирует ставку хеш-обязательством. PLAYER_REVEAL раскрывает сид игрока. HOUSE_REVEAL раскрывает сид дома и производит расчёт. Такое разделение не позволяет ни одной стороне увидеть результат до фиксации.

Что произойдёт, если игрок закроет браузер после ставки?

Сид игрока хранится только в памяти вкладки и будет утерян. После дедлайна раскрытия любой может отправить PLAYER_REVEAL_TIMEOUT, вернув ставку игроку и передав стоимость комнаты дому. Игрок потеряет только комиссию за исходную транзакцию JOIN.

Как генерируется случайность без доверенной третьей стороны?

Обе стороны фиксируют 32-байтовые сиды от CSPRNG через хеши BLAKE3 до того, как любой из них будет раскрыт. Результат броска — первые два байта BLAKE3(DICE_ROLL_V1 ‖ game_id ‖ house_seed ‖ player_seed), прочитанные как little-endian u16. Диапазон 0–65535 является степенью двойки, поэтому распределение абсолютно равномерное.

Можно ли играть в Arena Dice?

Нет. Arena Dice находится в разработке; постоянная продакшн-ветка отключена до завершения ревью. Уровень консенсуса прошёл третий независимый раунд проверки, но пользовательского интерфейса, публичных столов и боевого развёртывания нет. Arena Blackjack — действующий продукт Arena на сегодняшний день.

Что мешает дому отменять невыгодные ставки?

V2 заменил открытый сид в JOIN на обязательство на основе BLAKE3. В момент JOIN ни одна сторона не может вычислить результат броска — дом не может отличить выигрышные ставки от проигрышных. После раскрытия сида игроком у дома есть дедлайн; его пропуск запускает HOUSE_REVEAL_TIMEOUT, возвращая весь Game UTXO игроку.

Эту статью собрала, написала и оформила ИИ-команда OfficeForge — те же ИИ-сотрудники, что построили и ведут Kaspa Forge. Направляет основатель, проверено командой.

Некастодиально · открытый код

Держите KAS там, где кражу можно отменить

Ковенант-сейф в мейннете Kaspa: ваши ключи, ваши правила, наш инструмент. Ончейн — бесплатно, навсегда.

Создать сейф