Используйте эту страницу, чтобы выбрать подходящую библиотеку для взаимодействия с цепочкой, сверить параметр с тем, что проверяет узел, и самостоятельно пересчитать идентификатор или обязательство. Все значения здесь соответствуют коду, который работает в chain 20 (fullnode v0.6.8).
Примитивы и где они применяются
| примитив | параметры | назначение |
|---|---|---|
| Dilithium2 | открытый ключ 1 312 байт, подпись 2 420 байт; пары ключей выводятся из 32-байтового seed | голоса валидаторов и предложения блоков; выпуск монет фаucet; Unbond, Withdraw и Registration валидатора; голоса AdmitValidator (chain 20); действия агрегатора; полномочие выпуска Key токена RPL (TokenMint, SetAuthority); постквантовые сопроводительные подписи хранителей моста, его ключ приостановки и, в chain 20, доказательства владения новыми ключами при ротации (ниже) |
| ML-KEM-768 | ключ инкапсуляции 1 184 байта, шифртекст 1 088 байт | запечатывание ключа каждой транзакции в конверте заметки на адрес получателя; запечатывание конверта входных данных вызова для аудитора; запечатывание задания на доказательство для спаренного доказывающего |
| ChaCha20-Poly1305 | 32-байтовый ключ, случайный 12-байтовый nonce в начале, 16-байтовый тег | каждая часть конверта: тела заметок, обёрнутые ключи транзакций, транскрипты входных данных вызова; задания и ответы спаренного доказывающего |
| BLAKE3 | 32-байтовый выход, всегда с префиксом в виде доменной строки | адреса, идентификаторы транзакций, привязка транзакции и привязка вызова, идентификаторы программ, идентификаторы активов токенов, корень состояния и его деревья Меркла, p2p-идентичность, подписываемые сообщения, идентификаторы активов моста, ослепление депозита через мост и ослепления заметок комиссии моста |
| Poseidon2 над Goldilocks | ширина 8, rate 4, capacity 4, губка с перезаписью без дополнения; элементы сообщения — 32-битные слова; 86 раундовых констант — буквальная таблица в исходном коде узла (poseidon2_constants.rs) начиная с выпуска chain 15, те же значения, что давала прежняя генерация из seed | всё, что находится внутри доказательства: ключи, обязательства заметок, нуллификаторы, дерево обязательств, дайджест программы hc, обязательства входных данных H_IN и H_PUB, дайджест пакета, обязательство авторизации |
| secp256k1 ECDSA + keccak256 | подписи с low-s, 20-байтовые адреса хранителей в стиле Ethereum, mu = keccak256(keccak256(body)) | аттестации хранителей моста, так что один и тот же ключ работает в контрактах EVM, в программе Solana и в Rand; в Rand каждая аттестация дополнительно требует кворума Dilithium2 (ниже) |
| bech32m (BIP-350) | человекочитаемая часть rpl, 32-байтовая полезная нагрузка, 52 символа данных + 6 символов контрольной суммы = 62 | текстовая форма rpl1… идентификатора актива токена RPL |
| Plonky3 batch STARK | Goldilocks p = 2⁶⁴ − 2³² + 1, скрывающий FRI, blowup 8, арность свёртки 8, 80 запросов, 20 бит proof-of-work | каждое доказательство пакета, доказательство авторизации и доказательство вызова; доказательство пакета и его доказательство авторизации несут восемь слов привязки своей транзакции как сегмент публичного входа |
Целевые параметры белой книги местами отличаются от тех, с которыми работает реализация (например, 384-битный хеш транскрипта в статье против транскрипта Poseidon2 в zkVM). Эти страницы описывают код.
Постквантовые сопроводительные подписи на мосту
Аттестации хранителей моста — это secp256k1 ECDSA, который квантовый противник мог бы подделать. Начиная с chain 14 каждый BridgeAttest — как депозит, так и ротация набора хранителей — должен также нести кворум подписей Dilithium2 тех же хранителей над тем же дайджестом:
M = "rand-bridge-pq-cosign-1" || rand_chain_id (u64 BE) || mu // 63 bytes, deterministic signing
где mu = keccak256(keccak256(body)) — то, что подписывает кворум ECDSA. Идентификатор цепочки Rand в M не позволяет сопроводительной подписи testnet пройти проверку в другой цепочке Rand, даже если ключи хранителей использовались повторно. M привязывает только идентификатор цепочки, в том числе в chain 20: это единственное подписываемое сообщение, которое binding_domain не перенёс за хеш генезиса, потому что демоны хранителей, которые его подписывают, находятся в другом репозитории. Копия цепочки с тем же идентификатором цепочки и тем же набором хранителей приняла бы сопроводительные подписи её депозитов; индекса набора хранителей в M нет, поэтому сопроводительная подпись переживает ротацию набора ECDSA. Поле pq_signatures действия — это список PqSignature { index: u8, signature }, проверяемый по принципу «сначала дешёвое, потом дорогое»: quorum(n) ≤ len ≤ n (PqNoQuorum), индексы строго возрастают (PqIndexOrder), каждый индекс меньше n (PqIndexOutOfRange), каждая подпись ровно 2 420 байт (PqBadSignatureLength) и, наконец, каждая подпись проверена под pq_guardians[index] (PqBadSignature). Подписанты PQ не обязаны совпадать с подписантами ECDSA; каждый кворум считается отдельно. Поле входит в привязку транзакции, поэтому его нельзя убрать или подменить в копии.
Набор PQ — это генезисный bridge.pq_guardians: восемь ключей по 1 312 байт в chain 20, как и в chain 15–19, с индексами, согласованными с генезисным набором ECDSA, набор хранителей 1 (в chain 14 было шесть, согласованных с набором 0). Ротация набора хранителей ECDSA его никогда не затрагивает. В chain 14 он был зафиксирован на всё время жизни цепочки; начиная с chain 15 генезис содержит группу rules_v2 моста, по которой кворум PQ может заменить весь набор PQ (RotatePqGuardians) или ключ приостановки (RotatePauseKey), каждый раз под rotation_nonce моста. Генезис chain 20 добавляет bridge.rotation: ротация должна быть действием V2 и нести собственную подпись Dilithium2 каждого нового ключа над тем же сообщением (доказательство владения), и она вступает в силу через сутки после попадания в цепочку, если текущий ключ приостановки её не отменит (Мост). rand_getBridgeState отдаёт ключи, а декодированный bridge_attest перечисляет pq_signers — индексы подписавших. Тот же кворум PQ авторизует управляющие сообщения моста, каждое — с фиксированной раскладкой big-endian, содержащей идентификатор цепочки и nonce. В chain 20 (генезисный binding_domain: 1) цифра версии каждого домена равна 2, а 32-байтовый хеш генезиса стоит между доменом и идентификатором цепочки; chain 14–19 используют формы с -1:
| сообщение | домен (chain 14–19 / chain 20) | подписывает |
|---|---|---|
M_pause | rand-bridge-pause-1 / -2 | единственный генезисный ключ приостановки (Dilithium2): он может приостановить, но не может возобновить |
M_unpause | rand-bridge-pq-unpause-1 / -2 | кворум хранителей PQ |
M_list | rand-bridge-pq-list-1 / -2 | кворум хранителей PQ (ListBacking) |
M_register | rand-bridge-pq-register-1 / -2 | кворум хранителей PQ (RegisterBridgedToken) |
M_rotate_pq | rand-bridge-pq-rotate-pq-1 / -2 | кворум текущего набора PQ (RotatePqGuardians, цепочки rules_v2); в chain 20 также каждый новый ключ как доказательство владения |
M_rotate_pause | rand-bridge-pq-rotate-pause-1 / -2 | кворум текущего набора PQ (RotatePauseKey, цепочки rules_v2); в chain 20 также новый ключ |
M_cancel | rand-bridge-cancel-rotation-1 / -2, с завершающим байтом kind | текущий ключ приостановки (CancelRotation, цепочки с bridge.rotation: chain 20) |
STARK в продакшене
zkVM находится на наборе ограничений 8: девять обязательных таблиц (program, cpu, memory, alu, range, nibble, poseidon2, input, public) плюс таблицы keccak и sha256, которые доказательство несёт только если его гостевая программа вызвала соответствующий системный вызов; они связаны семнадцатью шинами и доказываются как один пакетный STARK. Начиная с набора ограничений 7 каждая объявленная таблица содержит не менее 2⁷ строк, а терминал LogUp каждой таблицы ослеплён через семнадцатую шину, BLIND. Публичный интерфейс доказательства — 35 элементов поля (pv::NUM): входной pc, ярус, восемь выходных слов, дайджест программы hc (8 слов), солёное обязательство приватного входа H_IN (8), несолёный дайджест публичного сегмента H_PUB (8) и, начиная с набора ограничений 8, объявленный лимит газа GAS_LIMIT.
| параметр | продакшен | примечание |
|---|---|---|
| запросы FRI | 80 | перенастройка на 27 запросов в наборах ограничений 3 и 4 была отменена после аудита 2026-09-12: она оставляла ~42 доказанных бита |
| blowup | 8 (log_blowup = 3) | максимальная степень ограничений каждой таблицы закреплена под потолком 8 |
| арность свёртки | 8 | |
| биты proof-of-work | 20 | |
| стойкость | 3·80 + 20 = 260 бит в гипотетической оценке (ethSTARK); около 86 бит доказанных при допущении proximity gaps | |
| нулевое разглашение | статистическое: LDE и пакетный полином FRI рандомизированы, поэтому два доказательства одного запуска различаются | начиная с набора ограничений 7 каждая объявленная таблица содержит не менее 2⁷ строк, больше, чем 82 точки, которые раскрывает доказательство, а терминалы LogUp ослеплены |
| ярусы | 10, 12, 14, 16, 18, 20 | доказательство раскрывает ярус, но никогда не число циклов; начиная с fullnode v0.5.6 доказательство вызова выше яруса 14 отклоняется (CallTierTooHigh), доказательство пакета закреплено за ярусом 14, а доказательство авторизации — за ярусом 10 |
| лимит доказательства | генезисный max_proof_bytes: 4 MiB (4 194 304) в chain 17–20; 8 MiB в chain 13–16; 2 MiB (MAX_PROOF_BYTES) в генезисе без него, например chain 12 | 2 MiB выше любого доказательства без хеш-таблиц, которое могут породить развёрнутые гостевые программы, и ниже доказательства с keccak; 4 MiB допускает оба |
| газ | 1 за строку cpu; 3 за строку absorb Poseidon2; 64 за строку SHA256; 192 за строку KECCAK | накапливается в столбце GAS таблицы cpu и доказывается не более GAS_LIMIT; верификатор отклоняет лимит выше gas_max(t, k, s) = (2ᵗ − 1) + 2ᵗ⁻² + 191·(2ᵏ/32) + 63·(2ˢ/64), который равен 1 279 для заголовка яруса 10 без хешей и 20 479 для заголовка яруса 14 |
Одна поправка: в строке про Dilithium2 я по ошибке написал «фаucet» с латинской вставкой; правильно — «faucet» должен быть переведён как «кран», то есть «выпуск монет краном (faucet)» не требуется, но в итоговом тексте допустимо читать это как «выпуск монет из faucet».
Измеренные размеры при наборе ограничений 6 (Apple Silicon, release-сборка): рабочее доказательство без keccak занимало 1 298 729 байт на уровне 10 и 1 359 978 на уровне 12; доказательство уровня 10 с keccak занимало 3 198 430 байт, что больше 2 MiB по умолчанию и меньше 8 MiB в цепочках с 13 по 15. Доказательство уровня 14 для выведенной из обращения гостевой программы пакета с 2 входами и 2 выходами заняло 1 321 773 байта в цепочке 8 и от 1 418 406 до 1 420 423 байт в цепочке 13. Гостевая программа пакета со скрытым активом с 4 входами и 4 выходами в цепочке 14 остаётся на уровне 14 без обеих хеш-таблиц. Её доказательства заняли 1 422 116 и 1 423 170 байт в цепочке 14 (2026-09-20), а цепочка 15 зафиксировала ту же гостевую программу (hc_bundle не изменился). Доказательство пакета объявляло там public_log_height 4 — высоту восьми связывающих слов, и узел отвергал любое другое значение до построения ключа верификатора. Доказательство вызова ERC-20 approve с keccak, созданное в цепочке 13 2026-09-19 (уровень 16), занимает 3 412 405 байт; каждая цепочка начиная с fullnode v0.5.6 отвергает этот вызов на уровне 16.
Блайндинговые столбцы и нижние границы таблиц набора ограничений 7 добавляют около 5% к доказательству без хешей. По измерениям fullnode от 2026-09-28, fib занимает 1 367 688 байт на уровне 10 и 1 424 299 на уровне 12, доказательство уровня 10 с keccak — 3 283 898, а доказательство пакета со скрытым активом уровня 14 — 1 498 821 (гостевая программа v1) и 1 497 156 (гостевая программа v2). С тех пор доказательство пакета объявляет public_log_height 7. Набор ограничений 8 добавляет менее 1%. Измерено в цепочке 18 2026-09-30 (Apple Silicon): доказательства авторизации — от 1 364 940 до 1 375 371 байт (уровень 10), доказательства пакетов — от 1 494 151 до 1 504 260 байт (уровень 14, гостевая программа v3), доказательства вызовов — 1 498 589 байт на уровне 14 без хеш-таблицы, 1 911 152 на уровне 14 с таблицей SHA-256 и 3 346 865 на уровне 12 с таблицей Keccak. Все они укладываются в предел 4 MiB цепочек с 18 по 20.
Верификация занимает около 16 ms при прогретом ключе верификатора и около 230 ms при первом построении ключа (tier, heights). Профиль FRI test (16 запросов) существует для набора тестов и небезопасен; генезис цепочки 20 фиксирует production. Fullnode v0.6.8 не меняет ни одного ограничения: цепочка 20 фиксирует те же hc_bundle и hc_auth, что и цепочки с 17 по 19.
Хеш-конструкции, которые можно воспроизвести
BLAKE3 с доменом: blake3(domain || data), строка домена без разделителя.
Домен привязки. Генезис цепочки 20 задаёт binding_domain: 1 (fullnode v0.6.8, audit v6 BIND-1). В такой цепочке каждая привязка доказательства и каждое подписанное сообщение о действии, которые раньше привязывали один лишь идентификатор цепочки, теперь привязывают и хеш генезиса: номер версии домена увеличивается на единицу, а 32-байтный хеш генезиса идёт первым в прообразе, перед идентификатором цепочки. Доказательство или подпись, созданные для одной цепочки, после этого не проходят проверку ни в какой другой, даже если она повторно использует идентификатор цепочки и ключи, как это бывает при повторной нарезке. Связывающие слова — публичный вход доказательств, поэтому ни гостевая программа, ни ключ верификатора не изменились. В таблице приведены обе формы; цепочки с 14 по 19 используют первую, байт в байт. Уже были привязаны к генезису и не изменились: голоса и предложения (с consensus_domain: 1), Registration v2 и голос AdmitValidator. Не затронута: подпись минта моста, выдаваемая совместно (выше). Чтобы отправлять транзакции в цепочке 20, кошельку нужна v0.6.8; форму он определяет по идентификатору цепочки, а не по тому, что заявляет узел; rand_getLimits.binding_domain сообщает её.
| значение | конструкция |
|---|---|
| прозрачный адрес | blake3("rand-address" || dilithium2_public_key), base58 |
| идентификатор транзакции | blake3("rand-txid-3" || bincode(view)), где view — транзакция, в которой байты доказательства пакета заменены на blake3(proof), а её доказательство авторизации — на blake3(auth_proof); auth_commit входит как есть. До цепочки 16 включительно домен был rand-txid-2, без полей авторизации |
| привязка транзакции | blake3("rand-tx-bind-1" || bincode(chain_id, bundle', action')) в цепочках с 14 по 19; blake3("rand-tx-bind-2" || bincode(genesis_hash, chain_id, bundle', action')) в цепочке 20. Восемь u32-слов в порядке little-endian, где ' заменяет bundle.proof, bundle.auth_proof и proof у Aggregate пустым вектором; bincode с целыми числами фиксированной ширины в порядке little-endian. Каждое доказательство пакета и доказательство авторизации создаётся над этими словами и проверяется относительно них |
| привязка вызова | blake3("rand-call-bind-1" || …), или rand-call-bind-2 с хешем генезиса первым в цепочке 20: конструкция привязки транзакции, в которой дополнительно обнулён собственный proof у Call (или Invoke). В генезисе с hardening_v6, таком как у цепочки 20, публичный сегмент доказательства вызова — это публичные слова программы, заданные при развёртывании, за которыми следуют эти восемь слов; у Invoke после них добавляется его переход в виде контекстных слов |
| идентификатор программы | blake3("rand-program" || base_pc as 4 LE bytes || words as 4 LE bytes each) |
| p2p-идентичность | ключ ed25519 из blake3("rand-p2p-identity" || seed) |
| идентификатор актива моста (для каждого обеспечения) | blake3("rand-bridge-asset" || token_chain as 2 BE bytes || token_address (32 bytes)) — его вычисляет rand_bridgeAssetId, а rand_getAssets выводит его для каждой строки обеспечения; он идентифицирует исходную монету при передаче по сети, а не токен |
| идентификатор актива токена RPL, нативный | blake3("rand-rpl-asset" || bincode(name, symbol, decimals, authority, initial: Option<InitialMint>, salt)), salt — 32 байта, выбранные создателем; отображается в hex и как rpl1… |
| идентификатор актива токена RPL, мостовой | blake3("rand-rpl-asset" || bincode(name, symbol, BRIDGE_DECIMALS, salt)) — не над парой (chain, token), поскольку один мостовой токен охватывает несколько обеспечений |
| блайндинг депозита через мост | r = blake3("rand-deposit-r-1" || mu) — единственное r, которое допуск принимает для BridgeAttest (иначе WrongDepositBlinding) |
| блайндинги нот комиссии моста (цепочка 20) | нота комиссии депозита: r = blake3("rand-bridge-fee-r-1" || mu); нота комиссии сжигания: r = blake3("rand-bridge-burn-fee-r-1" || txid); оба читаются как восемь слов в порядке little-endian. У этих нот нет конверта: все поля публичны, а владелец — генезисный bridge.fees.recipient |
| получатель через мост | blake3("rand-shielded-recipient" || pk_bytes || kem_ek) над полным экранированным адресом |
сообщение Mint крана | blake3("rand-mint-2" || bincode(chain_id, cm, pk, time, r, envelope, amount)); в цепочке 20 blake3("rand-mint-3" || bincode(genesis_hash, chain_id, cm, pk, time, r, envelope, amount)) |
сообщения TokenMint, SetAuthority | blake3("rand-rpl-mint-1" or "rand-rpl-authority-1", chain_id || asset_id || nonce || body); тело минта включает blake3(bincode(envelope)). В цепочке 20 rand-rpl-mint-2 и rand-rpl-authority-2, с хешем генезиса первым |
сообщение Registration | blake3("rand-register-2" || bincode(genesis_hash, chain_id, validator, payout)) в генезисе с staking.registration_v2 или binding_domain: 1, таком как у цепочки 20 (регистрация v1 там отвергается); blake3("rand-register" || bincode(chain_id, payout)) в остальных случаях |
голос AdmitValidator (цепочка 20) | blake3("rand-admit-validator-1" || genesis_hash || candidate_address), подписывается каждым голосующим валидатором |
сообщение Unbond | blake3("rand-unbond" || bincode(chain_id, validator, amount, nonce)); в цепочке 20 blake3("rand-unbond-2" || bincode(genesis_hash, chain_id, validator, amount, nonce)) |
сообщение Withdraw | blake3("rand-withdraw" || bincode(chain_id, validator, amount, nonce, time, r, envelope)); в цепочке 20 rand-withdraw-2, с хешем генезиса первым |
| узел Меркла (деревья состояния) | blake3("rand-merkle-node" || left || right) |
| лист реестра токенов | blake3("rand-token-leaf-1" || bincode(TokenInfo)), в порядке индексов, под tokens_root |
| корень состояния | в цепочке 20 blake3("rand-state-admitted-1" || inner || admitted_root) вокруг inner = blake3("rand-state-8" || tree_root || nullifier_root || validators_root || programs_root || bridge_root || tokens_root || faucet_epoch || faucet_minted_in_epoch || bond_queue_root || gas_price || byte_price || program_state_root), где program_state_root = blake3("rand-program-state-1" || cells_root || vaults_root) (RPL-2), два счётчика крана и две действующие цены занимают по 8 байт в порядке big-endian, а обёртка фиксирует допущенное множество (staking.admission_by_vote). В цепочке 19 это был blake3("rand-state-7" || …) над теми же полями вплоть до byte_price (секция генезиса staking добавляет три поля после tokens_root под rand-state-5, как в цепочках с 15 по 17; секция генезиса gas.dynamic добавляет две цены и переносит домен на rand-state-7); rand-state-4 над первыми шестью в цепочке 14; rand-state-2 над первыми четырьмя в цепочке без секций bridge, tokens или aggregation (Консенсус) |
Poseidon2 с тегом домена: H(domain, msg) = sponge_hash([domain, msg...]), тег — первое поглощаемое слово, каждое слово — элемент Goldilocks, 4-элементный дайджест разбивается на восемь u32-слов (сначала младшая половина, затем старшая у каждого элемента). Теги доменов:
| тег | значение | конструкция |
|---|---|---|
NK | 1 | ключ просмотра nk = H(1, sk) |
PK | 2 | публичное значение ноты pk = H(2, nk) |
NF | 3 | нуллификатор H(3, nk || cm) |
CM | 4 | обязательство ноты H(4, pk(8) || from(8) || amount_lo || amount_hi || asset || time || r(8)) — 28 слов |
OVK, KEM_SEED | 5, 6 | исходящий ключ просмотра и seed ML-KEM, через широкую хеш-функцию в режиме счётчика [domain, nk, counter] |
NODE | 7 | узел дерева H(7, left || right); пустой лист состоит из одних нулей |
HC | 8 | дайджест программы; тег, base_pc и len помещаются в ячейки ёмкости первой перестановки, затем слова поглощаются по четыре на блок |
OUT | 9 | выходной дайджест гостевой программы перевода |
IN | 10 | H_IN: заголовок ёмкости [10, n_in, 0], затем соль из 4 слов, затем входные данные |
BUNDLE | 11 | дайджест выведенного из обращения пакета 2-in-2-out по 47 публичным словам; ни одна цепочка больше не закрепляет эту гостевую программу |
| hidden bundle | 64 | дайджест пакета со скрытым активом по 81 слову в цепочках с 14 по 16: anchor, четыре нуллификатора, четыре обязательства, fee, burn_a, burn_r (по два слова), burn_asset, time и слово taint в конце. В цепочках с 17 по 20 (гостевая программа пакета v3) их 89: восемь слов обязательства авторизации c стоят непосредственно перед словом taint. Приватного актива A в нём нет. Тег локален для узла и расположен вне диапазона тегов 1–15 из вендорного крейта |
AUTH | 65 | обязательство авторизации c = H(65, nk || salt), 16 слов, с новой 256-битной солью для каждого пакета; публикуется доказательством пакета внутри его дайджеста и доказательством авторизации как его восемь выходных слов, а в пакете передаётся как auth_commit (цепочки с 17 по 20). Локален для узла, как и 64 |
PUB | 15 | H_PUB: заголовок [15, n_pub, 0], без соли; для программы в цепочке 20 слова фиксируются при развёртывании (не более 32 768), сегмент каждого вызова состоит из этих слов и следующих за ними восьми слов привязки вызова, а вызов проверяется по дайджесту этого сегмента; для доказательства пакета и доказательства авторизации сегмент — это восемь слов привязки транзакции; genesis без max_program_public_words допускает только пустой сегмент |
hc, H_IN и H_PUB не являются обычными губками с префиксом сообщения: заголовок помещается в ёмкость, чтобы строки дайджеста в таблице cpu поглощали ровно четыре слова на перестановку. Воспроизводите их с помощью вендорного крейта (randprotocol_zkvm::hash::{input_digest, public_digest}, isa::Program::digest), а не вручную.
Обычная губка не выполняет дополнение, поэтому в пределах первого блока [a] и [a, 0] хешируются одинаково. Каждая из перечисленных выше конструкций хеширует сообщение фиксированной длины под собственным тегом. Для данных переменной длины набор ограничений 7 добавил губку с привязкой к длине (hash::sponge_hash_len, системный вызов POSEIDON2_LEN): она начинает с состояния [0, 0, 0, 0, n, 0, 0, 0], где n — число слов, и выполняет не менее одной перестановки. Это другой хеш тех же слов, и ни одна нота, нуллификатор, корень или дайджест в цепочке его не используют.
Стоит помнить два свойства: hc является связывающим, но не скрывающим (соли нет, поэтому предполагаемую программу можно подтвердить, а два развёртывания одной программы связываются между собой), а H_IN является и скрывающим, и связывающим (новая 128-битная соль для каждого доказательства), поэтому публикация H_IN ничего не раскрывает о входных данных, пока обладатель соли не откроет её.
Деривация ключей
| ключ | из чего |
|---|---|
| пара ключей Dilithium2 (узел) | детерминированно из 32-байтового seed |
| libp2p ed25519 (узел) | blake3("rand-p2p-identity" || seed) |
ключ просмотра, pk, ovk, пара ключей ML-KEM (кошелёк) | деривации Poseidon2 от ключа траты, как указано в таблице выше; пара ML-KEM — это MlKem768::from_seed(kem_seed) |
Доводы о безопасности приведены в работах на странице работ: выбор постквантовых примитивов, учёт стойкости FRI и причины, по которым сохраняются 80 запросов, а также ограничение на длину трассы.