Кошельки и keystore
Создание кошелька NOXY из BIP-39 мнемоники, деривация ключа аккаунта ML-DSA-44 и зашифрованный keystore.
Кошельки и keystore
Кошелёк выводит ключ аккаунта ML-DSA-44 из BIP-39 мнемоники и хранит расширенный секретный ключ в keystore, зашифрованном AES-256-GCM. Сегодня публичны две реализации этой деривации: браузерный кошелёк и TypeScript-пакет @noxy/core. Мнемоника, созданная в одном, восстанавливает тот же аккаунт в другом.
Создание и восстановление
Кошелёк генерирует BIP-39 мнемонику (по умолчанию 24 слова), выводит ключ аккаунта ML-DSA-44 по пути по умолчанию m/44'/8800'/0'/0/0 и шифрует расширенный секретный ключ паролем. Пустой пароль отвергается.
Восстановление из мнемоники повторяет ту же деривацию, поэтому всегда даёт тот же ключ аккаунта. Восстановленный keystore получает свежие id, salt и nonce — байты отличаются от исходного файла, но ключ внутри идентичен.
Деривация
Путь состоит из шести сегментов и имеет фиксированную форму:
m/44'/8800'/account'/change/index
44' и 8800' обязательны (8800 — coin type NOXY). account hardened; change и index — обычные u32. Любая другая форма отвергается.
Из seed в ключ аккаунта:
bip39_seed = BIP39(mnemonic, bip39_passphrase) # 64 байта
account_seed = HKDF-SHA256(
salt = "NOXY-L0/wallet-seed/v1",
ikm = bip39_seed,
info = derivation_path,
len = 32)
keypair = ML-DSA-44.KeyGen_internal(xi = account_seed) # FIPS 204
Размеры ML-DSA-44: открытый ключ 1312 байт, подпись 2420 байт. Keystore хранит каноничный расширенный секретный ключ — 2560 байт.
Одна мнемоника и один путь всегда дают один и тот же ключ аккаунта. Изменение пути — включая change или index — даёт другой ключ и, значит, другой аккаунт.
account_id
ID аккаунта выводится из открытого ключа, не хранится:
account_id = HASH("account-id/v1",
BLAKE3(public_key) || account_type_u16_le || salt)
HASH(domain, msg) — это BLAKE3("NOXY-L0/v0.1/" || domain || 0x00 || msg). Кошельки используют account_type = 1 (User) и пустой salt.
ID аккаунта стабилен при ротации ключа: ротация подписывающего ключа его не меняет. Полная модель — на странице Аккаунты.
Адрес и verifier
32-байтный ID аккаунта рендерится тремя способами. Hex — каноничная wire-форма; bech32m и verifier — для отображения.
hex f63052cfff6e7141bf90b7f9170a589f0674771a2d44173549fe60ba6aebf251
bech32m tnxy17cc99nlldec5r0usklu3wzjcnur8gac694zpwd2flest56ht7fg…
verifier general · leisure · vote · 97
bech32m использует HRP noxy на mainnet и tnxy на testnet, с контрольной суммой BIP-350, отвергающей опечатки и смешанный регистр. Адрес, закодированный под одним HRP, не декодируется под другим.
Verifier — три слова BIP-39 плюс двузначное число, выведенные из ID аккаунта:
digest = BLAKE3("NOXY-L0/v0.1/account-verifier/v1\0" || account_id)
word_i = u16_le(digest[2i .. 2i+2]) & 0x07ff для i = 0,1,2
number = u16_le(digest[6..8]) % 100
Прочитайте четыре токена вслух, чтобы подтвердить получателя, вместо сравнения 64 hex-символов. Verifier — только отображение, транзакции связывают сырой ID аккаунта.
Имена аккаунтов
К аккаунту можно обращаться и по зарегистрированному имени вида alice@veylith. Имена живут в on-chain реестре с арендой (lease): имя регистрируется на срок, его можно продлить, перенацелить, передать или освободить. Кошелёк и SDK @noxy/core разрешают имена напрямую из состояния; публичного HTTP-эндпоинта резолва пока нет.
Формат keystore
{
"version": 1,
"id": "<uuid v4>",
"algorithm": "ML-DSA-44",
"public_key_hex": "…",
"derivation_path": "m/44'/8800'/0'/0/0",
"crypto": {
"cipher": "aes-256-gcm",
"ciphertext": "<hex>",
"nonce": "<hex, 12 байт>",
"kdf": "argon2id",
"kdfparams": {
"salt": "<hex, 16 байт>",
"memory_cost_kib": 19456,
"time_cost": 3,
"parallelism": 1,
"output_len": 32
}
}
}
Ключ шифрования — Argon2id (v1.3) над паролем с параметрами выше: 19 МиБ памяти, 3 прохода, 1 lane, 32-байтный выход. AES-256-GCM связывает метаданные keystore как associated data:
NOXY-L0/keystore/v1|id={id}|algorithm=ML-DSA-44|public_key={public_key_hex}|path={derivation_path}
Изменение id, public_key_hex или derivation_path ломает расшифровку. Plaintext — это 2560-байтный расширенный секретный ключ ML-DSA-44.
Расшифровка падает при неверном пароле, любом изменении ciphertext или поля AAD, либо при output_len != 32.
Что не входит
Это материал ключа аккаунта, а не готовый кастодиальный продукт. Нет guardian recovery, поддержки аппаратных кошельков или WalletConnect. Создание аккаунта в сети всё ещё требует отправки подписанной транзакции — кошелёк только производит ключ.