Kaspa Forge
Разбор

HD-кошелёк Kaspa: деривация и сканирование с разрывом

20 августа 2026 Автор — ИИ-команда OfficeForge · проверено командой 12 мин чтения
HD-деривация и сканирование с разрывом в кошельке Kaspa

Кошелёк Kaspa, хранящий средства в сейфах, эскроу-сделках и игровых комнатах Arena, нуждается в десятках ключей. Хранить каждый отдельно было бы ненадёжно: потеряешь один файл — и эти средства исчезнут навсегда. Стандартное решение — используемое биткоин-кошельками с момента BIP-32 и адаптированное здесь для UTXO-модели Kaspa — состоит в том, чтобы выводить каждый ключ из одного мастер-сида. Одна резервная копия покрывает всё: и прошлое, и будущее.

В этой статье рассказывается, как Kaspa Forge Desk реализует эту деривацию, как механизм восстановления с сканированием разрывов находит всю ончейн-активность, не передавая приватный ключ на сервер, и где в дизайне сделаны осознанные компромиссы.

Мастер-сид

Когда пользователь создаёт профиль Desk, браузер генерирует мастер-сид через WebCrypto getRandomValues. Этот сид — единый корень каждого ключа, который кошелёк когда-либо создаст. Он хранится внутри зашифрованного профиля (схема v3), который сам запечатан паролем, выбранным пользователем, с использованием формата шифрования age.

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

Определение

Доменно-разделённая деривация: техника, при которой один мастер-сид порождает полностью независимые наборы ключей путём подмешивания различной строки («домена») в каждый вызов деривации. Ключи хранилища, кошелька и Arena — все исходят из одного сида, но не могут быть перепутаны между собой.

Как работает деривация: пошагово

Ядро находится в модуле derive.rs крейта kaspa-safe-core на Rust, скомпилированном как в нативный код (серверные инструменты), так и в WebAssembly (браузерный Desk). Функция проста:

HMAC-SHA512(key = master_seed, message = "kaspaforge/v1/<domain>/<index>")

Первые 32 байта из 64-байтового выхода HMAC становятся производным секретным ключом. Оставшиеся 32 байта отбрасываются.

Строка домена — это то, что разделяет одно семейство ключей от другого:

Путь доменаНазначениеИспользуется
kaspaforge/v1/vault/0, …/1, …/2Ключи хранилищ сейфов (горячий, тревожный, пополнения)Создание сейфов, управление
kaspaforge/v1/wallet/0, …/1, …Адреса приёма кошелькаВкладка кошелька, отправка
arena/canary/dealer/0Идентичность дилера ArenaСоздание комнаты Arena
arena/canary/player/0Идентичность игрока ArenaПрисоединение к Arena
kaspaforge/v1/vault~token/0Ключи, связанные с токенами (планируется)Не активно
Чётная подпространство KasiaКлючи шифрования чатаЗашифрованный чат

Индекс после домена — простой возрастающий счётчик. Для ключей хранилища первый сейф использует индекс 0, второй — индекс 1, и так далее. Архитектурные заметки подтверждают: «первая резервная копия покрывает будущие сейфы» — потому что сид уже содержит материал для ключей, которые ещё не были выведены.

Это не BIP-44-деривация. Здесь нет разделения на hardened/unhardened, нет уровней purpose/coin/account. Схема путей специфична для Kaspa Forge, и вики Kaspa отмечает, что инструментарий разработчика Kaspa использует собственные соглашения, а не наследует полное дерево BIP-32 от биткоина. Компромисс — в пользу простоты и явной доменной изоляции за счёт кросс-кошельковой совместимости.

Почему доменная изоляция важна

Представьте, что было бы без неё. Если бы ключ хранилища с индексом 0 и ключ кошелька с индексом 0 выводились из одного входа HMAC, они оказались бы одним и тем же ключом. Транзакция, тратящая из кошелька, могла бы случайно захватить UTXO хранилища, а ход в игре Arena — списать обычный баланс пользователя.

Доменные строки делают это невозможным. HMAC для "kaspaforge/v1/vault/0" и "kaspaforge/v1/wallet/0" порождают несвязанные 256-битные ключи, хотя они разделяют один мастер-сид и один индекс. Подсистема Arena идёт дальше: её функция derive_canary_keyset создаёт ключи по путям arena/canary/dealer/0 и arena/canary/player/0, а результирующее хранилище дочерних ключей явно исключается из обычного набора адресов wallet/walletOld. Общая функция отправки не видит UTXO Arena, а подпись Arena не имеет доступа к средствам кошелька.

Пул адресов кошелька

Один профиль Desk может управлять множеством адресов. Текущий адрес приёма хранится в поле профиля wallet. Предыдущие адреса — до того, как пользователь сменил его — хранятся в walletOld (массив). Desk не требует ручной операции «свипа» или «сбора» при смене адреса. Вместо этого вкладка кошелька отображает баланс, который представляет собой сумму UTXO со всех выведенных адресов:

displayed_balance = Σ UTXOs(wallet) + Σ UTXOs(walletOld[0]) + Σ UTXOs(walletOld[1]) + …

Когда пользователь отправляет KAS, конструктор транзакций (build_wallet_split в WASM-ядре v9) выбирает UTXO из всего пула. Каждый вход подписывается ключом, которому он принадлежит — ключом, выведенным для конкретного адреса. Сдача всегда направляется на текущий адрес приёма, поэтому средства со временем естественным образом консолидируются без отдельного шага консолидации.

Этот дизайн отражает то, как UTXO-модель Kaspa работает на уровне протокола: в DAG нет «баланса счёта», только дискретные непотраченные выходы. Кошелёк лишь агрегирует их для отображения и расходования. Модель комиссий Kaspa взимает плату за каждый потреблённый UTXO, поэтому WASM-ядро Desk запрашивает свежую котировку комиссии у узла перед каждой отправкой и вычисляет точную комиссию как mass × quote без искусственного минимума.

Сканирование с разрывом: поиск активности при восстановлении

Вот основная проблема восстановления HD-кошелька: имея сид, как узнать, какие из выведённых адресов когда-либо получали средства?

Грубый подход — проверить каждый возможный индекс — непрактичен. Если пользователь использовал 5 адресов из огромного пространства ключей, сканирование всех из них тратит время и трафик. Стандартное решение — сканирование с лимитом разрыва: адреса выводятся по порядку, каждый проверяется на активность, и сканирование прекращается после обнаружения настраиваемого количества подряд неиспользованных адресов («разрыв»).

Desk реализует это в два слоя.

Слой 1: Клиентская деривация

Модуль restore.js в браузере берёт мастер-сид и начинает последовательно выводить адреса для каждого домена (ключи хранилищ, ключи кошелька). Для каждого индекса он:

1. Вычисляет деривацию HMAC-SHA512. 2. Извлекает публичный ключ. 3. Выводит адрес Kaspa.

Всё это происходит полностью в браузере. Приватные ключи не передаются.

Слой 2: Серверное сопоставление

Браузер отправляет пакеты публичных ключей — не адресов, не приватных ключей — на эндпоинт POST /api/safe/restore/scan. Серверный модуль restore_api.rs проверяет каждый публичный ключ на наличие известной активности. Ключевой момент: сервер сопоставляет по точной паре «публичный ключ + токен» — это не универсальный оракул, раскрывающий, существует ли произвольный адрес в блокчейне. Токен — это профильный токен пользователя, аутентифицирующий запрос без раскрытия сида.

Если совпадение найдено, сервер сообщает, какие индексы активны. Клиент тогда знает, что нужно продолжить сканирование дальше этой точки. Когда встречается полный разрыв из неиспользованных адресов, сканирование прекращается.

Кнопка «⟲ Пересканировать» в интерфейсе Desk запускает принудительное повторное сканирование. Автоматическое сканирование выполняется один раз на устройство при первой разблокировке.

Зачем помогает сервер (и чего он не видит)

Чисто клиентскому сканированию пришлось бы запрашивать узел Kaspa для каждого выведенного адреса. Узлы Kaspa индексируют набор UTXO, а не полную историю адресов. Проверка истории транзакций требует отдельного индексатора, к которому Desk обращается через прокси-эндпоинт (GET /api/safe/tx-history), чтобы не раскрывать IP клиента индексатору напрямую.

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

Как всё это работает вместе в продуктах Kaspa Forge

Дизайн с единым сидом и доменной изоляцией означает, что каждый продукт Kaspa Forge, которому нужны ключи, получает их из одного зашифрованного профиля:

  • Kaspa Safe: ключи хранилищ для горячего кошелька, тревожный ключ и ключ пополнения выводятся по пути kaspaforge/v1/vault/<index>. Каждый сейф получает свой индекс. Тревожный ключ может опционально храниться на физической карте полностью вне профиля.
  • Kaspa Escrow и Deposit: ключи эскроу-сделок используют ту же инфраструктуру деривации. WASM-ядро эскроу и продакшн-ядро Desk читают из одного мастер-сида.
  • Arena (публичная бета на мейннете): канареечные ключи для идентичностей дилера и игрока выводятся по путям arena/canary/dealer/0 и arena/canary/player/0. Эти ключи хранятся в поле arenaCanaryKeys зашифрованного профиля, но исключены из Forge Sync — они существуют только на устройстве, где были сгенерированы. Это осознанная граница безопасности: если синхронизированная копия профиля будет скомпрометирована, средства игр Arena не окажутся под угрозой.
  • Boards: подпись постов использует текущий ключ кошелька. Отдельный домен деривации не нужен, поскольку посты на досках — это стандартные подписи P2PK.
  • Marketplace: владение листингом привязано к идентичности профиля Desk. Сам маркетплейс не хранит средства — это делают эскроу-сделки — поэтому отдельный домен ключей не требуется.

Один сид, одна резервная копия — все продукты. Kaspa Forge Desk выводит все ключи — кошелька, хранилища, эскроу, Arena — из одного мастер-сида, хранящегося в вашем зашифрованном браузерном профиле. Экспорт .age-файла — единственная резервная копия, которая вам нужна. Восстановите его на любом устройстве, запустите сканирование с разрывом — и каждый сейф, сделка и адрес снова появятся. Ключи никогда не покидают ваше устройство. Открыть Desk →

Создать сейф

Компромиссы и честные границы

Собственная схема путей, а не BIP-44. Пути деривации kaspaforge/v1/… несовместимы с другими кошельками Kaspa. Если вы импортируете свой сид в другое приложение-кошелёк, оно не найдёт ваших средств, если только не реализует ту же схему доменной деривации HMAC-SHA512. Это осознанный компромисс: явная доменная изоляция и простота в ущерб совместимости. CLI-утилита vaultctl с открытым исходным кодом и опубликованное WASM-ядро документируют точную деривацию для всех, кто строит совместимые инструменты.

Лимит разрыва конечен. Сканирование прекращается после настраиваемого количества подряд неиспользованных адресов. Если пользователь сгенерировал множество адресов, не используя их — например, многократно нажимая «новый адрес» при тестировании, — разрыв может быть превышен, и некоторые старые активные адреса могут быть пропущены. Кнопка «⟲ Пересканировать» существует для этого случая, но требует, чтобы пользователь знал, что что-то могло быть упущено.

Сервер видит публичные ключи при восстановлении. Эндпоинт сканирования узнаёт, какие публичные ключи принадлежат данному профильному токену. Это компромисс в пользу приватности: альтернатива — запрос к узлу для каждого выведенного адреса — раскрывает ещё больше информации (IP плюс паттерн адресов) индексатору. Серверный подход концентрирует уязвимость в одном аутентифицированном запросе.

Канареечные ключи Arena привязаны к устройству. Forge Sync реплицирует зашифрованный профиль между устройствами, но явно исключает arenaCanaryKeys. Если вы смените устройство, идентичность Arena не перенесётся автоматически. Это защищает игровые средства от компрометации через синхронизацию, но означает, что игрокам Arena необходимо заново установить идентичность на новом устройстве.

Нет ротации ключей без смены адреса. UTXO-модель Kaspa означает, что «ротация ключа» эквивалентна генерации нового адреса приёма. Старые адреса остаются действительными, и их UTXO можно потратить, но средства не перемещаются автоматически. Агрегация кошельком по wallet и walletOld решает это для пользователя прозрачно, но пул адресов монотонно растёт.

Версионирование WASM-ядра. Логика деривации живёт в derive.rs, скомпилированном в версионированные WASM-снимки (v7, v8, v9). Продакшн-Desk использует v9. Каждый снимок неизменяем после развёртывания — новая схема деривации потребовала бы новой директории версии и явной миграции. Закреплённые тесты с известными векторами в derive.rs защищают от случайного дрейфа между версиями.

---

Фундаментальная идея в том, что единый сид в сочетании с доменно-разделённой HMAC-деривацией способен безопасно разграничить ключи по несвязанным продуктовым плоскостям — кошелёк, хранилище, эскроу, игра — без возможности какой-либо из них подделать ключи другой. Сканирование с разрывом превращает сид из теоретического механизма в практический инструмент восстановления. Компромиссы — собственные пути, конечный разрыв, серверное сканирование — видимы и задокументированы, и именно так должна работать система с самостоятельным хранением: вы можете проверить каждое утверждение по открытому исходному коду.

FAQ

Что такое HD-деривация кошелька Kaspa?

HD-деривация (иерархическая детерминированная) генерирует все ключи кошелька из одного мастер-сида с помощью детерминированной функции. В Kaspa Forge Desk для этого используется HMAC-SHA512 с доменно-разделёнными путями, чтобы ключи хранилища, кошелька, эскроу и Arena никогда не пересекались.

Как работает сканирование с разрывом при восстановлении кошелька Kaspa?

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

Можно ли восстановить кошелёк Kaspa из резервной копии, созданной до добавления новых сейфов?

Да. Поскольку все ключи детерминированно порождаются из одного мастер-сида, резервная копия сида — хранящаяся в .age-файле — способна воссоздать каждый ключ, созданный как до, так и после момента копирования. Затем сканирование с разрывом обнаруживает, какие из выведенных адресов имеют ончейн-активность.

Используют ли ключи игр Arena тот же путь деривации, что и ключи кошелька?

Нет. Канареечные ключи Arena используют отдельные доменные пути (arena/canary/dealer/0, arena/canary/player/0), поэтому обычная функция отправки кошелька не может случайно потратить UTXO Arena, а подпись Arena не имеет доступа к средствам обычного кошелька.

Отправляется ли мой приватный ключ на сервер при сканировании восстановления?

Нет. Браузер локально выводит адреса и отправляет только публичные ключи. Сервер сопоставляет их с известной активностью по точной паре «публичный ключ + токен» — это не универсальный оракул, раскрывающий, существует ли адрес в блокчейне.

Что произойдёт, если я сменю адрес приёма — потеряю ли я доступ к старым адресам?

Нет. Desk суммирует UTXO со всех выведенных адресов (wallet и walletOld). Каждый UTXO тратится своим собственным ключом в рамках одной транзакции. Отдельная операция «свипа» не требуется.

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

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

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

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

Создать сейф