Rand Protocol
Документация
Руководства

Криптография

Примитивы и места их применения, параметры STARK и FRI в промышленной эксплуатации, а также конструкции хеширования, которые разработчику может понадобиться воспроизвести.

Используйте эту страницу, чтобы выбрать подходящую библиотеку для взаимодействия с цепочкой, сверить параметр с тем, что проверяет узел, и самостоятельно пересчитать идентификатор или обязательство. Все значения здесь соответствуют коду, который работает в 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-Poly130532-байтовый ключ, случайный 12-байтовый nonce в начале, 16-байтовый тегкаждая часть конверта: тела заметок, обёрнутые ключи транзакций, транскрипты входных данных вызова; задания и ответы спаренного доказывающего
BLAKE332-байтовый выход, всегда с префиксом в виде доменной строкиадреса, идентификаторы транзакций, привязка транзакции и привязка вызова, идентификаторы программ, идентификаторы активов токенов, корень состояния и его деревья Меркла, 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 STARKGoldilocks 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_pauserand-bridge-pause-1 / -2единственный генезисный ключ приостановки (Dilithium2): он может приостановить, но не может возобновить
M_unpauserand-bridge-pq-unpause-1 / -2кворум хранителей PQ
M_listrand-bridge-pq-list-1 / -2кворум хранителей PQ (ListBacking)
M_registerrand-bridge-pq-register-1 / -2кворум хранителей PQ (RegisterBridgedToken)
M_rotate_pqrand-bridge-pq-rotate-pq-1 / -2кворум текущего набора PQ (RotatePqGuardians, цепочки rules_v2); в chain 20 также каждый новый ключ как доказательство владения
M_rotate_pauserand-bridge-pq-rotate-pause-1 / -2кворум текущего набора PQ (RotatePauseKey, цепочки rules_v2); в chain 20 также новый ключ
M_cancelrand-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.

параметрпродакшенпримечание
запросы FRI80перенастройка на 27 запросов в наборах ограничений 3 и 4 была отменена после аудита 2026-09-12: она оставляла ~42 доказанных бита
blowup8 (log_blowup = 3)максимальная степень ограничений каждой таблицы закреплена под потолком 8
арность свёртки8
биты proof-of-work20
стойкость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 122 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, SetAuthorityblake3("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, с хешем генезиса первым
сообщение Registrationblake3("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), подписывается каждым голосующим валидатором
сообщение Unbondblake3("rand-unbond" || bincode(chain_id, validator, amount, nonce)); в цепочке 20 blake3("rand-unbond-2" || bincode(genesis_hash, chain_id, validator, amount, nonce))
сообщение Withdrawblake3("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-слов (сначала младшая половина, затем старшая у каждого элемента). Теги доменов:

тегзначениеконструкция
NK1ключ просмотра nk = H(1, sk)
PK2публичное значение ноты pk = H(2, nk)
NF3нуллификатор H(3, nk || cm)
CM4обязательство ноты H(4, pk(8) || from(8) || amount_lo || amount_hi || asset || time || r(8)) — 28 слов
OVK, KEM_SEED5, 6исходящий ключ просмотра и seed ML-KEM, через широкую хеш-функцию в режиме счётчика [domain, nk, counter]
NODE7узел дерева H(7, left || right); пустой лист состоит из одних нулей
HC8дайджест программы; тег, base_pc и len помещаются в ячейки ёмкости первой перестановки, затем слова поглощаются по четыре на блок
OUT9выходной дайджест гостевой программы перевода
IN10H_IN: заголовок ёмкости [10, n_in, 0], затем соль из 4 слов, затем входные данные
BUNDLE11дайджест выведенного из обращения пакета 2-in-2-out по 47 публичным словам; ни одна цепочка больше не закрепляет эту гостевую программу
hidden bundle64дайджест пакета со скрытым активом по 81 слову в цепочках с 14 по 16: anchor, четыре нуллификатора, четыре обязательства, fee, burn_a, burn_r (по два слова), burn_asset, time и слово taint в конце. В цепочках с 17 по 20 (гостевая программа пакета v3) их 89: восемь слов обязательства авторизации c стоят непосредственно перед словом taint. Приватного актива A в нём нет. Тег локален для узла и расположен вне диапазона тегов 1–15 из вендорного крейта
AUTH65обязательство авторизации c = H(65, nk || salt), 16 слов, с новой 256-битной солью для каждого пакета; публикуется доказательством пакета внутри его дайджеста и доказательством авторизации как его восемь выходных слов, а в пакете передаётся как auth_commit (цепочки с 17 по 20). Локален для узла, как и 64
PUB15H_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 запросов, а также ограничение на длину трассы.

Последняя проверка по коммиту fullnode c0878c3f (chain 21) от 2026-10-12. Если код и эта страница расходятся, прав код; сообщите нам.