Эта страница нужна для интеграции с мостом: чтобы построить депозит в исходной сети, отправить аттестацию в Rand, сжечь мостовой актив обратно и прочитать публичное состояние моста. Chain 20, текущая testnet (работает с 2026-10-01 06:17 UTC, fullnode v0.6.8), была запущена с секцией bridge: восемь гардианов, начиная с индекса набора гардианов 1, восемь постквантовых гардианов, ключ паузы, эмиттеры для Ethereum, BSC, Tron и Solana (первые три — эндпоинты, переразвёрнутые 2026-09-30), группа rules-v2, в которой глобальный лимит чеканки составляет 4,000 zUSD за скользящие 24 часа, нижняя граница защиты от повторов для входящих сообщений и две группы, новые в v0.6.8: комиссии в размере 10 bps на каждую чеканку и каждое сжигание, взимаемые в zUSD, и правила ротации, по которым ротация постквантового набора или ключа паузы должна доказывать владение новыми ключами и ждать один день. В отличие от генезиса chain 14, её генезис включает zUSD со всеми семью обеспечениями и переносит custody, которое перешло от chain 14 через каждый запуск (Перенесено с chain 14). rand_getAssets и /bridge/assets в обозревателе показывают текущее значение locked для каждого обеспечения.
Схема потока
source chain (Ethereum / BSC / Tron / Solana) Rand
┌────────────────────────────────┐ ┌────────────────────────────────┐
│ lock tokens, emit │ guardians observe, │ BridgeAttest transaction │
│ (amount, token, to, fee) │ sign mu = keccak²(body) │ admission → one deposit note │
└────────────────────────────────┘ threshold n·2/3 + 1 │ computed by the chain │
▲ + Dilithium2 co-signers │ │
release after guardian guardian committee │ BridgeBurn transaction │
signatures on the burn ◄───── secp256k1 keys ◄───────── │ one bundle: slots 0–1 burn the │
message read the burn log │ asset, 2–3 pay the RAND fee; │
│ emits a sequenced burn message │
└────────────────────────────────┘
Входящее направление: ценность блокируется в исходной сети, гардианы выдают аттестацию, любой желающий отправляет аттестацию в Rand, и реестр добавляет одну депозитную ноту для получателя — ноту, в слове asset которой стоит индекс мостового токена в реестре. Исходящее направление: транзакция Rand сжигает ноты этого актива и порождает сообщение; гардианы выдают аттестацию на это сообщение; контракт в исходной сети выпускает средства. Rand никогда не проверяет заголовок или доказательство состояния исходной сети: подписи комитета гардианов составляют всю модель доверия.
Модель доверия и идентификаторы сетей
Локальные для моста идентификаторы сетей, общие с контрактами (bridge-codec):
| сеть | id |
|---|---|
| Rand | 1 |
| Ethereum | 2 |
| BSC | 3 |
| Tron | 4 |
| Solana | 5 |
Гардианы — это комитет ключей secp256k1; идентичность гардиана — последние 20 байт keccak256(uncompressed_pubkey[1..]), то есть способ получения адреса в Ethereum, поэтому один ключ работает в контрактах EVM, в программе Solana и в Rand. Кворум для набора размера n равен n·2/3 + 1. Каждая подпись должна быть low-s (s ≤ n/2), что исключает malleability. Дайджест, который подписывают гардианы: mu = keccak256(keccak256(body_bytes)). Ротация набора гардианов — это аттестация от эмиттера управления (chain 1, keccak256("rand-bridge-governance")); она должна содержать new_index == current + 1 и быть подписана текущим набором, а не устаревшим; уходящий набор сохраняет льготное окно в 86,400 секунд для переводов, находящихся в пути. (Это набор ECDSA. Постквантовый набор и ключ паузы ротируются собственными действиями, которые в chain 20 выдерживают задержку в один день: ниже.) Если n·2/3 + 1 гардианов вступят в сговор, они смогут отчеканить произвольную ценность; Усиление защиты моста ограничивает, сколько и как быстро, но не устраняет эту возможность.
Привязка транзакции. Транзакция Rand не несёт подписи: сжигание авторизуется STARK-доказательством её пакета. Начиная с chain 14 каждое доказательство пакета строится над Transaction::binding и проверяется относительно него — хэшем всей транзакции (идентификатор сети, единственный пакет с четырьмя конвертами, действие), в котором лишь байты доказательства обнулены. Начиная с chain 17 пакет несёт два доказательства: доказательство пакета и доказательство авторизации траты; binding обнуляет оба, сохраняет auth_commit пакета, и оба проверяются относительно него. Копия ожидающего сжигания с изменёнными to, relayer_fee или (to_chain, token), либо копия attest с изменёнными time или конвертом не проходит проверку доказательства (TxError::InvalidBundleProof). В chain 20 binding также включает хэш генезиса (генезис binding_domain: 1: rand-tx-bind-2 над хэшем генезиса, идентификатором сети, пакетом и действием), поэтому доказательство, сделанное для другой сети с тем же идентификатором, тоже не проходит проверку. Кошельки и релееры должны запускать бинарный файл, собранный для сети, в которую они отправляют: v0.6.8 в chain 20 (v0.6.7 в chain 18 и 19); более старый rand создаёт доказательства и подписи, которые chain 20 отвергает.
Демоны гардиана и релеера не входят в полный узел; полный узел содержит общий кодек и принимающую половину.
Формат передачи
Все целые числа — big-endian; версия формата — 1.
Конверт аттестации
| смещение | длина | поле |
|---|---|---|
| 0 | 1 | version = 1 |
| 1 | 4 | guardian_set_index |
| 5 | 1 | n_sigs |
| 6 | 66·n_sigs | подписи: index (1), r (32), s (32), v (1) |
6 + 66·n_sigs | … | body |
Заголовок тела — 51 байт, затем полезная нагрузка
| смещение | длина | поле |
|---|---|---|
| 0 | 4 | timestamp |
| 4 | 4 | nonce |
| 8 | 2 | emitter_chain |
| 10 | 32 | emitter_address |
| 42 | 8 | sequence |
| 50 | 1 | consistency_level |
| 51 | … | payload |
Полезная нагрузка Transfer — id 1, 133 байта
| смещение | длина | поле |
|---|---|---|
| 0 | 1 | id = 1 |
| 1 | 32 | amount (u256; Rand использует младшие 128 бит, и депозит должен также помещаться в u64 ноты) |
| 33 | 32 | token_address |
| 65 | 2 | token_chain |
| 67 | 32 | to — хэш получателя, см. ниже |
| 99 | 2 | to_chain |
| 101 | 32 | fee (u256): доля amount, причитающаяся релееру |
Полезная нагрузка GuardianSetUpgrade — id 2: id (1), new_index (4), n (1), затем n 20-байтовых ключей.
Адреса Ethereum, BSC и Tron — 20 байт, дополненные слева 12 нулевыми байтами до 32-байтовых слотов; адрес Solana заполняет слот целиком. Transfer должен приходить от зарегистрированного эмиттера сети emitter_chain, а token_chain должен совпадать с сетью-источником. Аттестация занимает не более 16 KiB. Усиление защиты моста не изменило ни формат передачи, ни кодек, ни развёрнутые эндпоинты исходных сетей: постквантовые сопроводительные подписи и сообщения управления передаются в транзакции Rand, а не в аттестации.
32-байтовый получатель
Защищённый адрес занимает 1,216 байт, а в формате передачи есть место лишь для 32. Поле to несёт его хэш:
to = blake3("rand-shielded-recipient" || pk_bytes || kem_ek) // ShieldedAddress::recipient_hash
Депозитор указывает хэш в исходной сети, гардианы его подписывают, отправитель помещает полный адрес в действие BridgeAttest, а реестр пересчитывает хэш и отвергает несовпадение — иначе отправитель мог бы выбирать, кто получит чужой депозит. Интерфейсы исходных сетей должны принимать эту форму; кошелёк её выводит. Chain 11 ненадолго сделала слот непосредственно идентификатором получателя; эта форма адреса была отменена вместе с chain 12 (Счета и адреса).
Один токен, много обеспечений: zUSD
Реестр активов больше не принадлежит мосту. Он перешёл в реестр токенов RPL на уровне реестра, общий для нативных и мостовых токенов, и корень состояния сети фиксирует его как tokens_root. Мостовой токен — это запись реестра, чьё право чеканки — Bridge { backings }, и он больше не соответствует одной паре (chain, token). Первый из них, zUSD, обеспечен USDT и USDC, заблокированными в четырёх сетях: Ethereum, Binance Smart Chain и Solana несут обе монеты, Tron — только USDT (Circle прекратила выпуск USDC в Tron):
chain token (32-byte wire form) coin decimals
2 000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7 USDT 6
2 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48 USDC 6
3 00000000000000000000000055d398326f99059ff775485246999027b3197955 USDT 18
3 0000000000000000000000008ac76a51cc950d9822d68b83fe1ad97b32cd580d USDC 18
4 000000000000000000000000a614f803b6fd780986a42c78ec9c7f77e6ded13c USDT 6
5 ce010e60afedb22717bd63192f54145a3f965a33bb82d2c7029eb2ce1e208264 USDT 6
5 c6fa7af3bedbad3a3d65f36aabc97431b1bbe4c2d2f6e0e47ca60203452f5d61 USDC 6
В chain 20 zUSD имеет индекс 1, rpl1xtj6k2x8strx8c2d5fjs50ltztck5ykms4ve7nmzmstf6fhn0v0spelqtx, название “Shielded USD”, 8 знаков после запятой, и указан в файле генезиса, как в chain 15–19, с названием, символом и солью, которые он имел в chain 14, поэтому его идентификатор актива (32e5ab28c782c663e14da2650a3feb12f16a12db85599f4f62dc169d26f37b1f) и идентификатор rpl1… перешли без изменений (обозреватель: GET /tokens/1). В chain 14 он был зарегистрирован на высоте 256 транзакцией register_bridged_token.
Backing { chain: u16, token: [u8; 32], decimals: u8, locked: u64, minted_today: u64, mint_day: u32 }
Пара (chain, token) обеспечивает не более одного токена во всём реестре (TokenError::BackingTaken); у токена от 1 до 32 обеспечений; decimals — это собственное число знаков исходной монеты, от 0 до 18, но никогда не 8 мостового токена. Объединённое обеспечение — заявленный риск: семь монет взаимозаменяемы с zUSD по номиналу при входе и выходе, поэтому потеря привязки любой из них размывает каждого держателя zUSD, а ограничения на долю одного обеспечения на стороне Rand нет.
zUSD — это корзина. Держатель несёт риск каждого из перечисленных обеспечений, а не только той монеты, которую внёс: один zUSD — это требование к пулу, на любое обеспечение с достаточным locked. Ни одно обеспечение не оценивается. Блокировка 1 единицы любой перечисленной монеты чеканит 1 zUSD, а сжигание может указать любое обеспечение, чей locked его покрывает. Поэтому монету, потерявшую привязку к курсу, можно внести по номиналу, а zUSD сжечь в пользу монеты, сохранившей привязку, пока надёжные обеспечения не будут исчерпаны. Убыток ограничен скользящими окнами чеканки и фактически заблокированным хранением. В сети chain 20 за любые 24 часа можно начеканить не более 4 000 zUSD по всем обеспечениям вместе и не более 100 000 по каждому обеспечению (правила v2). Через надёжное обеспечение не может уйти больше, чем оно содержит. Окно ограничивает скорость, а не общий объём: оно даёт время приостановить чеканку, что прекращает новые вклады слабой монеты, пока сжигание остаётся открытым. Лимита погашения на отдельное обеспечение и паузы для отдельного обеспечения нет. В сети chain 14, до правил v2, единственным ограничением были собственный пауза-ключ исходных конечных точек и лимиты скорости по токенам.
Инвариант: total_supply == Σ backings.locked. TokenRegistry::lock (BridgeAttest) за один вызов добавляет подтверждённую сумму к locked обеспечения и к total_supply токена, а TokenRegistry::release (BridgeBurn) вычитает освобождаемую сумму из обоих. Больше ничто не записывает ни одно из этих полей, а лист токена фиксирует locked каждого обеспечения в tokens_root, поэтому два узла, расходящиеся в одном из них, расходятся в корне состояния. Исходные конечные точки не берут комиссию при блокировке и удерживают всю подтверждённую сумму при освобождении, так что locked каждого обеспечения равен хранению в этой конечной точке с поправкой на сообщения в пути. Комиссии в chain 20 этого не нарушают: комиссия — это нота zUSD, которая остаётся выпущенной и обеспеченной, поэтому вклад блокирует полную сумму, а сжигание освобождает только amount − fee. В chain 20 собственная комиссия конечных точек в 10 bps на USDT при освобождении была обнулена при запуске сети; теперь комиссия взимается в Rand.
Суммы в сети передаются в единицах с 8 знаками после запятой, независимо от собственной разрядности исходной монеты: вклад 1 USDT из Ethereum (6 знаков) блокируется как 100_000_000 единиц. Только индекс 0, RAND, имеет девять знаков этой сети.
Токен должен быть внесён в список до первого вклада. Регистрации при первом появлении больше нет: подтверждение, называющее (chain, token), которого никто не внёс в список, отклоняется с BridgeError::UnlistedToken (token … of chain … is not listed on this chain). Средства остаются на хранении в исходной конечной точке, пока кворум постквантовых хранителей не внесёт монету в список (Листинг после генезиса).
asset_id = blake3("rand-bridge-asset" || token_chain (2 bytes BE) || token_address (32 bytes))
rand_bridgeAssetId(token_chain, token_address) по-прежнему вычисляет это значение в любой сети, и именно к нему приводится пара (token_chain, token_address) в полезной нагрузке Transfer, но ключом реестра оно больше не является. Нота и кошелёк несут индекс токена — плотное u32: индекс 0 — это RAND, и в реестре его никогда нет. В rand_getAssets одна строка на каждое обеспечение, и все строки одного токена имеют один индекс:
[{ "index": 1, "chain": 2, "token": "000000000000000000000000dac17f…", "asset_id": "…", "decimals": 6,
"locked": "0", "mint_cap_per_day": "10000000000000", "minted_today": "0", "mint_day": 20716 }]
locked, mint_cap_per_day и minted_today — десятичные строки в единицах с 8 знаками, как и каждая сумма u64, которую RPC отдаёт начиная с chain 14; index, chain, decimals и mint_day остаются числами. Методы уровня токена — rand_getTokens, rand_getToken, rand_getTokenSupply — возвращают мостовой токен одной строкой со всеми его обеспечениями (токены RPL). GET /bridge/tokens обозревателя — это статический список разрешённых монет, которые принимает мост.
Входящее направление: BridgeAttest
Action::BridgeAttest { attestation: Vec<u8>, recipient: ShieldedAddress, r: Word8, time: u32,
asset: u32, envelope: Envelope, pq_signatures: Vec<PqSignature> }
PqSignature { index: u8, signature: Vec<u8> } // Dilithium2, 2,420 bytes
Передаётся обычной экранированной транзакцией, единственный пакет со скрытым активом которой оплачивает базовую комиссию 0,001 RAND и ничего более: её релейер платит за вкладчика, у которого ещё нет RAND. Цепочка, а не отправитель, вычисляет ноту вклада { pk: recipient.pk, from: 0, amount, asset: <index>, time, r } из суммы, подписанной хранителями, поэтому релейер не может ни завысить, ни перенаправить чеканку; её обязательство — единственный лист, который транзакция не несёт в сети. В chain 20 amount ноты равен полной сумме за вычетом комиссии за чеканку, а цепочка добавляет вторую ноту — комиссию — в казначейство (Комиссии за чеканку и сжигание).
Ослепляющий множитель выводится, а не выбирается. r должен быть равен
r = blake3("rand-deposit-r-1" || mu) // read as eight little-endian words; mu = keccak256(keccak256(body))
дайджесту, подписанному кворумом хранителей; любое иное значение даёт BridgeError::WrongDepositBlinding и отклоняется до всякой работы с подписями. Поле остаётся в сети — публичная пересборка кошелька читает его оттуда — оно лишь ограничено, и правило распространяется и на attest ротации. До этого любой, кто видел ожидающую транзакцию релейера, мог подать то же подтверждение со своим ослепляющим множителем: получатель всё равно получал ноту, но транзакция релейера погибала после того, как он уже заплатил за доказательство. При фиксированном r два отправителя одного подтверждения с одним time называют одну и ту же ноту, и пул объявляет их конфликтующими по обязательству.
time и asset — два предсказания вкладчика, сделанные для того, чтобы конверт можно было запечатать до появления транзакции. time ограничен тем же окном, что и у пакета (1 024 блока в chain 20, из proof_window_blocks генезиса; 256 до этого), и намеренно не выводится: никакое правило не задаёт time, с которым согласился бы каждый отправитель, поэтому опережающий отправитель всё ещё может выбрать другой time в окне — другую ноту того же получателя, суммы и актива, которую кошелёк получателя находит тем же способом, — и две подачи сталкиваются уже на дайджесте подтверждения. asset должен совпадать с индексом, который реестр уже присвоил (chain, token), иначе TxError::AttestAssetMismatch.
Поле fee полезной нагрузки Transfer передаётся и никому не выплачивается: платить некому, поскольку личности отправителя нет, поэтому вклад равен полной сумме за вычетом только собственной комиссии цепочки за чеканку там, где её задаёт генезис (chain 20). Защита от повторов устроена по дайджесту: каждый использованный mu попадает в множество spent моста, а мемпул отклоняет второго релейера, подающего то же подтверждение. Вклад также ограничен лимитом чеканки обеспечения, а в chain 20 — и глобальным лимитом, и отклоняется, пока чеканка приостановлена (B1, правила v2). Оба лимита считают полную сумму. В сети, генезис которой задаёт порог повторов, включая chain 20, перевод с sequence ниже порога его исходной цепочки отклоняется с BelowReplayFloor до проверки каких-либо подписей (Перенесено из chain 14).
rand bridge-mint <attestation hex or @file> --pq <JSON or @file> [--to rand1…] [--fee <RAND>] [--no-wait]
rand bridge-rotate <rotation hex or @file> --pq <JSON or @file> [--fee <RAND>] [--no-wait]
--pq обязателен: кворум сопроводительных подписей Dilithium2 хранителей в виде [{"index":0,"signature":"<4840 hex>"},…]. До любого доказательства кошелёк декодирует подтверждение, проверяет кворум по pq_guardians узла и идентификатору цепочки, проверяет, что получатель хешируется в подтверждённого, и читает индекс из списка; затем он выводит r, запечатывает конверт получателя (на чистую сумму в сети с комиссиями) и платит одним пакетом из собственных RAND. bridge-rotate подаёт ротацию набора хранителей (полезная нагрузка 2), которая ничего не вносит, и отклоняет всё, что не продвигает набор Rand ровно на один шаг. Поскольку каждое поле ноты вклада публично в этой единственной транзакции, кошелёк получателя пересобирает адресованные ему ноты вкладов, обходя зафиксированные транзакции bridge_attest, без вскрытия какого-либо конверта: враждебный релейер может запечатать мусор, но не может скрыть ноту.
Исходящее направление: BridgeBurn
Transaction {
chain_id,
bundle: <hidden-asset bundle: slots 0–1 spend the token, slots 2–3 pay the RAND fee;
burn_asset = asset, burn_a = amount, burn_r = 0, fee >= BRIDGE_BURN_FEE>,
action: BridgeBurn { asset, amount, relayer_fee, to_chain, token, to }
}
Начиная с пакета со скрытым активом chain 14, сжигание — это один пакет (раньше было два): слоты 0–1 доказывают, что сжигающий владел нотами актива на сумму не меньше amount, где burn_a — значение, покидающее пул, а слоты 2–3 оплачивают комиссию в RAND. Комиссия — BRIDGE_BURN_FEE, 0,01 RAND, она покрывает базу пакета. relayer_fee — часть amount (fee ≤ amount), которую контракт освобождения выделяет в целевой цепочке, а не дополнительный сбор, поэтому burn_a равно ровно amount. В chain 20 цепочка также удерживает комиссию за сжигание в 10 bps от amount в виде ноты zUSD в казначейство, а освобождается amount − fee; часть релейера должна укладываться в это освобождение (Комиссии за чеканку и сжигание).
Сжигание называет свою монету. (to_chain, token) должно быть обеспечением сжигаемого актива (иначе TokenError::NotABacking), и две проверки выполняются на шаге действия, до доказательства пакета:
- Единица освобождения. Обеспечение с
d < 8знаками исходной монеты освобождается единицами по10^(8−d)(100 для монеты с 6 знаками; 1 для монеты с 18 знаками). Освобождаемая сумма иrelayer_feeдолжны быть кратны ей, иначеTokenError::NotReleasable(… is not a multiple of the release unit …): дробь меньше наименьшей единицы исходной монеты осталась бы запертой на хранении. - Собственное хранение обеспечения. Освобождаемая сумма
≤ lockedэтого обеспечения, иначеTokenError::InsufficientBacking(only … is locked in that backing, not the … asked for). Общее предложение токена может покрывать освобождение, но покрыть его должно хранение именно этого обеспечения.
Освобождаемая сумма равна amount в сети без комиссий и amount − fee в chain 20.
to должен иметь правильную форму (20 байт с дополнением нулями в цепочках 2, 3 и 4; никогда не из одних нулей). Реестр записывает BridgeBurnRecord под строго возрастающим burn_sequence, с хешем транзакции в слоте отправителя, и хранители читают журнал сжиганий:
→ {"method":"rand_getBridgeBurn","params":[13]}
← { "sequence": 13, "amount": "99900000", "fee": "99900", "release_amount": "99800100",
"body_hex": "…", "digest": "a4bc1c7d…63f4", "tx": "ceb9e3c2…0be7", "height": 2899 }
body_hex — это исходящее сообщение в точном виде, в котором стражи (guardians) должны его хешировать и подписывать; сумма его Transfer равна release_amount. Запись выше — первое сжигание в chain 20: 0.999 zUSD сожжены в направлении BSC USDT на высоте 2,899, из них 0.000999 ушли в казначейство и 0.998001 были высвобождены.
rand bridge-burn <ASSET> <AMOUNT> <TO_CHAIN> <TOKEN> <TO> [--relayer-fee N] [--fee <RAND>] [--no-wait] [--yes]
rand bridge-message <SEQUENCE> # one outbound message, verbatim, for a guardian to sign
Сначала кошелёк проверяет — по одному чтению состояния моста и реестра токенов, до какого-либо доказательства, — что в цепочке есть мост, что ASSET есть в реестре, что TOKEN в TO_CHAIN обеспечивает его и заблокирован в объёме не менее AMOUNT, а также что AMOUNT и комиссия релейера выражены целым числом единиц высвобождения. В цепочке с комиссиями он затем выводит комиссию моста и сумму, которая будет высвобождена, и спрашивает подтверждения перед доказательством (--yes пропускает вопрос). После этого сжигание доказывается одним пакетом: около 100 с на ноутбуке и около 1.5 MB доказательства, а в chain 20, как и для каждого пакета начиная с chain 17, рядом с ним идёт доказательство авторизации траты (несколько секунд, около 1.37 MB).
Комиссии при выпуске и сжигании
Новое в fullnode v0.6.8; задаётся в chain 20 группой генезиса
"bridge": { …, "fees": { "mint_bps": 10, "burn_bps": 10, "recipient": "rand1…" } }
цепочка берёт комиссию с каждого мостового выпуска и каждого мостового сжигания в мостовом токене, в Rand, в виде экранированной ноты на адрес казначейства recipient. Каждая ставка не превышает 100 bps (1 %); в chain 20 обе равны 10 bps, то есть 0.1 %. Комиссия равна ⌊amount · bps / 10⁴⌋, округлённому вниз до целой единицы высвобождения обеспечения (10^(8 − source decimals): 100 единиц для USDT с 6 знаками, 1 для монеты с 18 знаками), поэтому и комиссия, и остаток — целые числа единиц, а комиссия, округлившаяся до нуля, ноты не создаёт (ledger/bridge_notes.rs, bridge_fee).
депозит (BridgeAttest) на брутто-сумму g | сжигание (BridgeBurn) на amount | |
|---|---|---|
| комиссия | ⌊g · mint_bps / 10⁴⌋, вниз до единицы высвобождения | ⌊amount · burn_bps / 10⁴⌋, вниз до единицы высвобождения |
| сторона пользователя | нота депозита на g − fee | сожжено amount (burn_a == amount, без сдачи гостевой программы); исходящее тело удостоверяет release_amount = amount − fee, и конечная точка высвобождает ровно эту сумму |
| нота казначейства | fee, blinding blake3("rand-bridge-fee-r-1" ‖ mu), с time и активом депозита | fee, blinding blake3("rand-bridge-burn-fee-r-1" ‖ txid), с time пакета |
locked и total_supply | растут на g | падают на release_amount |
| проверяется по | лимитам выпуска по обеспечению и глобальному лимиту, на g | InsufficientBacking и NotReleasable на release_amount; доля релейера должна в неё укладываться (FeeExceedsAmount) |
Обе ноты выпущены и обеспечены, поэтому равенство total_supply == Σ locked == custody по-прежнему выполняется: нота комиссии — такой же zUSD, как и любой другой, и её стоимость остаётся заблокированной в исходной цепочке, пока кто-нибудь её не сожжёт. BRIDGE_BURN_FEE — 0.01 RAND, которые сжигание платит в своих слотах RAND, — не изменилась и существует отдельно.
Первый круговой путь chain 20, по данным узла: депозит 1 zUSD из BSC на высоте 1,664 (4f6e6c54…1a64) дал вкладчику 0.999 zUSD, а казначейству 0.001; сжигание 0.999 zUSD обратно в BSC USDT на высоте 2,899 (порядковый номер 13) оставило 0.000999 zUSD и высвободило 0.998001. После него (чтение 2026-10-01) обеспечение BSC USDT составляло 22.001999 zUSD, а предложение токена, 99.001999 zUSD, равнялось сумме его обеспечений.
Как казначейство находит свои ноты. У ноты комиссии нет конверта: каждое её слово публично (сумма — из транзакции и ставки генезиса, актив и time — из транзакции, blinding выводится, владелец — recipient из генезиса). Кошелёк, чей адрес — recipient, восстанавливает ноты комиссий в том же публичном проходе, что и ноты депозитов, читая rand_getBridgeState.fees и decimals обеспечений один раз за синхронизацию. Узел, лгущий о комиссиях, может лишь сделать так, что нота останется ненайденной, но не может зачислить ни одной. Кошелёк вкладчика оценивает собственный депозит так же: брутто за вычетом комиссии. Так делает только кошелёк v0.6.8; более старый в любом случае не может отправлять в chain 20.
Без группы, в цепочках с 14 по 19, комиссии нет: депозит зачисляет брутто-сумму, а сжигание высвобождает amount. До chain 20 единственной комиссией моста был собственный сбор конечных точек источника — 10 bps с USDT при высвобождении, в цепочке-хосте; при выпуске chain 20 он был установлен в 0. Группа зафиксирована в хеше генезиса и входит в корень моста под rand-bridge-fees-1. Кодирование описано в docs/bridge.md fullnode, §25.
Усиление защиты моста
locked по каждому обеспечению и защита InsufficientBacking охраняют погашение. Они не ограничивают выпуск: аттестация с действительным кворумом стражей выпускает то, что в ней указано. Этот пробел закрывают три слоя, а четвёртый позволяет внести токен в список без выпуска новой цепочки. Ни один из них не меняет конечные точки в исходной цепочке, формат аттестации в сети или bridge-codec.
B1: лимит выпуска по обеспечению и пауза выпуска
tokens.mint_cap_per_day в генезисе применяется к каждому обеспечению: chain 20 задаёт 10000000000000, то есть 100,000 zUSD на обеспечение в сутки, как и каждая цепочка начиная с 14. Без rules v2 (chain 14) сутки — это timestamp_ms / 86_400_000 от временной метки блока; при rules v2 (цепочки с 15 по 20) тот же лимит измеряется по скользящему окну cap_window_secs времени блока, а глобальный лимит реестра — 4,000 zUSD за 86,400 с в chain 20 — является более жёсткой границей (rules v2). Депозит сверх лимита отклоняется с TokenError::MintCapExceeded до работы с подписями стражей и допускается, как только в окне снова появляется место.
bridge.pause_key в генезисе — один ключ Dilithium2, отличный от каждого постквантового стража. Два действия без пакета и без комиссии:
PauseMints { nonce, signature }— подписывается одним лишь ключом паузы. Он может только приостановить.UnpauseMints { nonce, pq_signatures }— требует постквантового кворума стражей, но никогда не ключа паузы.
Оба несут и увеличивают pause_nonce, поэтому ни одно из них нельзя воспроизвести повторно. Пока выпуск приостановлен, каждый передающий BridgeAttest отклоняется с BridgeError::MintsPaused; сжигания и ротации набора стражей остаются открытыми, поэтому пауза никогда не запирает погашение. При rules v2 (цепочки с 15 по 20) внесение в список во время паузы тоже отклоняется. Ключ паузы предполагается хранить на другой машине, нежели ключи стражей.
rand bridge-pause --sig <HEX or @file> [--no-wait] # no key file and no RAND needed
rand bridge-unpause --pq <JSON or @file> [--no-wait]
Что покрывает пауза: демоны стражей, которых обманом или взломом заставили совместно подписать поддельный выпуск, тогда как держатели постквантового кворума к этому непричастны. Она не защищает от кражи самого кворума: тот может снять паузу, внести в список до 32 обеспечений для одного токена, у каждого со своим суточным лимитом, а при rules v2 — заменить постквантовый набор и ключ паузы.
B2: ограниченные временные метки блоков
Временная метка блока в цепочке с мостом определяет истечение набора стражей и сутки лимита выпуска, поэтому лидер не должен иметь возможности её подвинуть скачком. В цепочке с мостом действует следующее:
- Правило валидности, проверяемое и при воспроизведении:
timestamp_ms ≤ parent.timestamp_ms + 60_000, иначеBlockError::TimestampLeap. Вместе с существующим правилом против отката (BlockError::TimestampRewind) время блока движется только вперёд, не более чем на минуту за блок. - Правило голосования, но не правило воспроизведения: валидатор не отдаёт голос за блок, опережающий его собственные часы более чем на 15,000 мс.
Поэтому каждому валидатору в цепочке с мостом нужен NTP: тот, чьи часы расходятся более чем на 15 секунд, — это и неисправный лидер, и неисправный голосующий. Пока цепочка нагоняет отставание после остановки, время блока может продвигаться до 60 с за блок, так что сутки лимита выпуска могут смениться быстрее реальных суток — не более чем на один дополнительный суточный лимит за каждые полные сутки накопленного отставания.
B3: сопровождающая подпись Dilithium2 на каждом выпуске
Каждый BridgeAttest, будь то депозит или ротация, должен нести рядом со своим кворумом ECDSA кворум подписей Dilithium2 над
M = "rand-bridge-pq-cosign-1" || rand_chain_id (u64 BE) || mu // 63 bytes
mu — дайджест, который подписывает кворум ECDSA; идентификатор цепочки не позволяет сопровождающей подписи testnet пройти проверку в другой цепочке Rand. Подписи передаются в pq_signatures действия, внутри привязки транзакции, поэтому их нельзя удалить или подменить в копии. Проверка идёт от дешёвого к дорогому: quorum(n) ≤ len ≤ n (PqNoQuorum), индексы строго возрастают (PqIndexOrder), каждый индекс < n (PqIndexOutOfRange), каждая подпись ровно 2,420 байт (PqBadSignatureLength) и, наконец, каждая проверяется по pq_guardians[index] (PqBadSignature). Два кворума считаются независимо. bridge.pq_guardians в генезисе выровнен по индексам с набором ECDSA генезиса (набор 1 в chain 20). Ротация payload-2 перемещает только набор ECDSA; без rules v2 постквантовый набор неизменен на всю жизнь цепочки, а при rules v2 его заменяет только RotatePqGuardians (в chain 20 — его форма V2, с доказательством владения и суточной задержкой). В chain 20 их восемь, те же восемь ключей, что и в цепочках с 15 по 19; для выпуска нужны шесть из них (в chain 14 было шесть, а требовалось пять).
Эта сопровождающая подпись по-прежнему привязана только к идентификатору цепочки, и в chain 20 тоже: в отличие от приведённых ниже governance-сообщений, она не была перенесена за genesis-хеш, поскольку демоны guardian, которые её формируют, находятся в другом репозитории и не менялись. Копия цепочки с тем же идентификатором, скопировавшая и набор guardian, приняла бы сопровождающие подписи депозита, сделанные для оригинала; пакет комиссий самой депозитной транзакции в chain 20 привязан к genesis, а аттестация внутри него — нет.
B4: листинг мостового токена после genesis
Обычный адрес никогда не может создать токен с полномочием Bridge. Два действия, каждое на обычном пакете комиссий в RAND и каждое под постквантовым кворумом:
RegisterBridgedToken { name, symbol, salt, chain, token, decimals, nonce, pq_signatures }— новый мостовой токен под следующим индексом, 8 знаков после запятой в Rand, под лимитом эмиссии из genesis, с его первым обеспечением. Комиссия: базовая комиссия пакета плюсregistration_feeреестра (1 RAND в chain 20, то есть 1,001 RAND; chain 20 сжигает регистрационную комиссию, а не выплачивает её предлагающему блок).ListBacking { token_index, chain, token, decimals, nonce, pq_signatures }— дополнительное обеспечение зарегистрированного мостового токена, начиная сlocked = 0. Комиссия: базовая комиссия пакета.
Оба несут и увеличивают list_nonce (иначе BadListNonce { expected, got }) и выполняют одни и те же проверки: для цепочки зарегистрирован эмиттер (BridgeError::NoEmitter), (chain, token) пока ничего не обеспечивает, обеспечений меньше 32, decimals ≤ 18.
rand token register-bridged --name … --symbol … --salt <hex32> --chain N --token <hex32> --decimals N --pq <JSON or @file>
rand token list-backing --asset <index> --chain N --token <hex32> --decimals N --pq <JSON or @file>
Два правила порядка. Guardian сначала подписывают регистрацию, ждут её фиксации, считывают присвоенный индекс и только затем подписывают листинги, которые на него ссылаются: регистрация нативного токена не требует разрешений, поэтому листинг, подписанный заранее, мог бы обнаружить, что его индекс уже занят. И монета листингуется в Rand до того, как setToken включит её на исходной конечной точке: в обратном порядке депозит мог бы заблокировать средства раньше, чем у Rand появится обеспечение для зачисления, и его аттестация отклонялась бы с UnlistedToken, пока Rand не догонит.
Governance-сообщения
Фиксированные байты, не bincode; все целые числа в порядке big-endian:
M_pause = "rand-bridge-pause-1" || chain_id u64 || nonce u64
M_unpause = "rand-bridge-pq-unpause-1" || chain_id u64 || nonce u64
M_list = "rand-bridge-pq-list-1" || chain_id u64 || nonce u64 || token_index u32 || chain u16 || token [32] || decimals u8
M_register = "rand-bridge-pq-register-1" || chain_id u64 || nonce u64 || u8 len || name || u8 len || symbol || salt [32] || chain u16 || token [32] || decimals u8
M_rotate_pq = "rand-bridge-pq-rotate-pq-1" || chain_id u64 || rotation_nonce u64 || count u32 || key [1312] … // rules v2
M_rotate_pause = "rand-bridge-pq-rotate-pause-1" || chain_id u64 || rotation_nonce u64 || key [1312] // rules v2
M_pause/M_unpause несут pause_nonce, M_list/M_register — list_nonce, а M_rotate_pq/M_rotate_pause — rotation_nonce. count — это число ключей, а не длина в байтах; каждый ключ — это 1 312 байт открытого ключа Dilithium2 в исходном виде, в том порядке, в котором они будут стоять в новом наборе.
В chain 20 каждое сообщение привязано к genesis-хешу (genesis binding_domain: 1): цифра версии в теге становится 2, а 32-байтовый genesis-хеш стоит между тегом и идентификатором цепочки, остальная часть не меняется — M'_pause = "rand-bridge-pause-2" || genesis [32] || chain_id u64 || nonce u64, и так же rand-bridge-pq-unpause-2, rand-bridge-pq-list-2, rand-bridge-pq-register-2, rand-bridge-pq-rotate-pq-2 и rand-bridge-pq-rotate-pause-2 (bridge/gov.rs). Ключ паузы или кворум, подписывающий для одной цепочки, после этого не подписывает ни для какой другой цепочки с тем же идентификатором и теми же ключами. Файл, подписанный в форме …-1, в chain 20 отклоняется (BadPauseSignature, PqBadSignature). Цепочки с 14 по 19 используют приведённые выше раскладки …-1 байт в байт.
Правила v2: ротации, скользящие лимиты, запрет листинга во время паузы
Необязательная группа внутри секции bridge в genesis. В chain 14 её не было; во всех цепочках начиная с 15 она есть, в chain 20 — с теми же значениями:
"bridge": { …, "rules_v2": { "global_mint_cap_per_window": 400000000000, "cap_window_secs": 86400 } }
- Две ротации под постквантовым кворумом.
RotatePqGuardians { new_pq_guardians, nonce, pq_signatures }заменяет весь постквантовый набор;RotatePauseKey { new_pause_key, nonce, pq_signatures }заменяет ключ паузы. Обе не требуют пакета и комиссии, какPauseMints, обе подписываются текущим постквантовым набором, и обе несут и увеличиваютrotation_nonce, так что один подписанный кворум разрешает ровно одну ротацию. Новый набор должен совпадать по длине с текущим набором ECDSA, состоять из ключей по 1 312 байт, без повторов и не содержать ключ паузы. Льготного окна нет: прежний набор или ключ после этого ничего не подписывает. В chain 20 эти действия v1 отклоняются, а ротация должна доказать владение новыми ключами и ждать сутки (ниже). - Скользящие лимиты по окну. Лимит
mint_cap_per_dayдля каждого обеспечения измеряется на скользящем окнеcap_window_secs, а не по календарному дню, и второй, общий для реестра лимитglobal_mint_cap_per_windowохватывает все обеспечения всех токенов вместе (TokenError::GlobalMintCapExceeded). В chain 20 он равен 400 000 000 000 единиц, то есть 4 000 zUSD за 86 400 с. При дневном правиле лимит в 23:59:59 и снова в 00:00:01 давал удвоенный лимит за две секунды; при оконном правиле второе действие отклоняется. - Запрет листинга во время паузы.
RegisterBridgedTokenиListBackingотклоняются сMintsPaused, покаmint_paused. Сжигания и обе ротации остаются открытыми.
Ротация: доказательство владения и задержка в сутки
Правила v2 оставили открытыми три вещи: ничто не доказывало, что новыми ключами кто-либо владеет (опечатка или ключ, которого ни у кого нет, навсегда вывели бы постквантовый кворум из строя), ротация вступала в силу в том же блоке, в который попала, и ничто не могло остановить враждебную ротацию. Вторая необязательная группа рядом с rules_v2 (которую она требует), появившаяся в v0.6.8 (audit v6, BRG-14), закрывает их. Chain 20 её задаёт:
"bridge": { …, "rotation": { "delay_secs": 86400, "needs_possession": true } }
- Доказательство владения. Ротация должна быть
RotatePqGuardiansV2 { new_pq_guardians, possession, nonce, pq_signatures }илиRotatePauseKeyV2 { new_pause_key, possession, nonce, pq_signatures }:possession[i]— собственная подпись Dilithium2 нового ключаiнад тем же сообщением, которое подписывает кворум (M_rotate_pqилиM_rotate_pause, в их форме…-2в chain 20), по одной на каждый ключ в порядке ключей, по 2 420 байт каждая. Действия v1 отклоняются сPossessionRequired. Неверное число или длина отклоняются до проверки кворума; неверная подпись владения (BadPossession { index }) — после неё. - Задержка. Принятая ротация записывается как ожидающая, с
effective_at_secs = block time + delay_secs, и вступает в силу в конце первого блока, чья метка времени достигает этого значения. В chain 20 это 86 400 с, то есть сутки. До тех пор старый набор по-прежнему подписывает каждую эмиссию, а старый ключ паузы по-прежнему ставит паузу. Принятие ротации расходует одинrotation_nonce, а её активация — ещё один, поэтому аттестация эмиссии, находящаяся в пуле под старым набором, покидает пул при смене набора. Одновременно может ожидать по одной ротации каждого вида (RotationPending { effective_at_secs }). - Отмена.
CancelRotation { kind, nonce, signature }(без пакета и комиссии) снимает ожидающую ротацию видаkind(0 — постквантовый набор, 1 — ключ паузы). Её подписывает текущий ключ паузы — единственный ключ, хранящийся отдельно от кворума, — над"rand-bridge-cancel-rotation-2" || genesis [32] || chain_id u64 || rotation_nonce u64 || kind u8в chain 20, и она расходует nonce. Поэтому захваченный кворум не может сменить набор быстрее чем за сутки, а владелец ключа паузы может остановить это в течение суток; входящий ключ паузы не может отменить собственное вступление.
Подписант — rand-node bridge-gov (rotate-pq, rotate-pause, cancel-rotation, у каждой команды есть шаги message, sign и submit; submit повторно проверяет каждую строку владения перед отправкой). rand_getBridgeState отдаёт rotation_rules и pending_rotations; оба равны null в цепочке без этой группы (цепочки с 14 по 19). Ротация набора guardian на ECDSA (payload 2) не затрагивается: у неё остаются собственные правила и льготное окно. В chain 20 ни одна ротация не проводилась.
Унаследовано от chain 14
Chain 15 была создана из позиции моста chain 14 (те же эмиттеры и исходные конечные точки), а chains 16, 17 и 18 — каждая таким же образом из позиции предшественницы. Chain 19 была создана из позиции chain 18 с одним изменением: её эмиттеры Ethereum, BSC и Tron — это конечные точки, переразвёрнутые 2026-09-30 (0x7af6b17047c1db6cb54347fdea45cf9179075bfa для chains 2 и 3, 0x6410797df959987a5baf65b5fab97edeb34d5163 для chain 4). Chain 18 им никогда не доверяла, поэтому ничего из них в ней не эмитировалось. Chain 20 была создана из снимка chain 19 на высоте 7 258, с предварительно остановленным мостом, и сохраняет эмиттеры, guardian, постквантовые ключи и ключ паузы chain 19 без изменений; она добавляет группы комиссий и ротации. Эмиттер Solana и эмиттер на стороне Rand не менялись с chain 14. Что содержит genesis chain 20:
- Хранение. 99 zUSD, в виде двух генезисных нот zUSD (10 и 89 zUSD), против 22 USDT, заблокированных в Ethereum, 22 USDT в BSC, 30 USDT в Tron и 25 USDT в Solana, и 0 по каждому обеспечению в USDC:
lockedцепочки 19 на момент снимка, совпавший с хранением на конечных точках.total_supply == Σ lockedравно хранению на конечных точках в блоке 0. История: 10 zUSD, оставшиеся в цепочке 14, были одной генезисной нотой в цепочках с 15 по 19; генезис цепочки 18 содержал их как 9 USDT, заблокированных в Tron, и 1 в Solana, а генезис цепочки 19 — как 10 в Solana после ребалансировки в цепочке 18. Остальные 89 — это чистые депозиты цепочки 19 за вычетом её сжиганий. - Набор хранителей с индексом 1, в котором уже находятся исходные конечные точки; следующая ротация ECDSA должна нести индекс 2. Восемь постквантовых ключей и ключ паузы — те же, что в цепочке 15, без изменений.
burn_sequence13: сжигания цепочки 14 закончились на 6, генезис цепочек с 15 по 18 начинался с 7, цепочка 18 выполнила одно сжигание (7), а цепочка 19 начала с 8. Цепочка 19 выполнила пять сжиганий, последовательности с 8 по 12, поэтому цепочка 20 начинается с 13, и ни одна конечная точка или демон, индексируемые по последовательности, не увидят одну и ту же дважды. Первое сжигание цепочки 20 получило последовательность 13.- Защита от повторного воспроизведения. Выпуск (mint) более ранней цепочки по-прежнему прошёл бы кворум ECDSA, но его постквантовая соподпись привязана к идентификатору этой цепочки, поэтому в цепочке 20 он отклоняется. В генезисе цепочки 15 не было нижней границы для входящих последовательностей (
min_inbound_sequenceбыл равенnull), так что там защита держалась на соподписи и на том, что хранители никогда не соподписывают старую блокировку для нового идентификатора цепочки. Начиная с цепочки 16 генезис задаёт такую границу: для каждой исходной цепочки — наименьшийsequence, который может нести аттестация перевода от эмиттера этой цепочки. В цепочке 20 она равна{ "2": 3, "3": 3, "4": 3, "5": 7 }: следующая исходящая последовательность каждой конечной точки на момент снимка цепочки 19, причём каждая более ранняя блокировка уже была выпущена в цепочке 19. (В цепочке 18 она была{ "2": 2, "3": 2, "4": 2, "5": 2 }; в цепочке 19 —{ "2": 1, "3": 1, "4": 1, "5": 4 }, где последовательность 0 на каждой заново развёрнутой конечной точке была собственной блокировкой оператора, которая никогда не должна выпускать токены.) Перевод ниже границы отклоняется с ошибкойBelowReplayFloor, какие бы кворум и соподпись он ни нёс. Ротации наборов хранителей по этой границе не оцениваются. Граница фиксируется в генезисе, входит в хеш генезиса и включается в корень моста.
Порядок запуска в цепочке 14 (история)
Как мост был запущен в цепочке 14, более ранней testnet, в сентябре 2026 года:
- Создать цепочку с разделом
bridge(включаяpq_guardiansиpause_key) и разделомtokens, задающимregistration_feeиmint_cap_per_day, без перечисления токенов. Выполнено 2026-09-20. - Включить кран (faucet), чтобы кошелёк-деплоер мог получить RAND. Выполнено.
- Деплоер регистрирует zUSD с первым обеспечением под подписью кворума над
M_registerприlist_nonce = 0. Выполнено на высоте 256. - Хранители считывают индекс и подписывают шесть оставшихся сообщений
ListBacking, отправляемых по одному. Выполнено:list_nonceравен 7. - Для каждого обеспечения: листинг в Rand раньше, чем
setTokenна исходной конечной точке. - Ротация набора хранителей (набор 0 → набор 1, подписана 5 из 6 текущим набором) на четырёх исходных конечных точках, затем та же аттестация в Rand. Rand показывал набор 0 всё время жизни цепочки 14; цепочка 15 начинается с набора 1.
- Живой круговой перевод для каждой исходной цепочки на малых суммах — ввод через мост, выпуск zUSD, перевод, сжигание в обратную сторону — и сверка
lockedкаждого обеспечения (rand_getTokenSupply) с хранением на конечной точке.
Публичное состояние
rand_getBridgeState:
{
"enabled": true,
"emitter": "01…", "emitters": { "2": "02…" },
"guardian_set_index": 0, "guardians": ["aabb…"],
"pq_guardians": ["…"],
"mint_paused": false, "pause_nonce": 0, "list_nonce": 0, "pause_key": "…",
"registration_fee": "1000000000",
"burn_sequence": 1,
"rotation_nonce": 0,
"rules_v2": { "global_mint_cap_per_window": "400000000000", "cap_window_secs": 86400,
"global_minted_in_window": "…", "global_mint_headroom": "…" },
"min_inbound_sequence": null,
"rotation_rules": null, "pending_rotations": null,
"fees": null,
"assets": [ … ]
}
pq_guardians — открытые ключи Dilithium2, по 1 312 байт каждый в шестнадцатеричном виде. next_index больше нет: токен, переданный через мост, листингуется под индексом, зафиксированным при его регистрации, до того как его можно внести, так что предсказывать нечего. rotation_nonce — это то, что должны нести следующие M_rotate_pq/M_rotate_pause/M_cancel; rules_v2 равно null в цепочке без этой группы (цепочка 14), а min_inbound_sequence равно null в цепочке без нижней границы воспроизведения (цепочка 15 и ранее); в цепочке 20 оно равно { "2": 3, "3": 3, "4": 3, "5": 7 }. rotation_rules, pending_rotations и fees равны null в цепочке без этих групп (цепочки с 14 по 19). Пример выше показывает структуру, а не значения цепочки 20: цепочка 20 началась с guardian_set_index 1 и burn_sequence 13 и отдаёт "rotation_rules": { "delay_secs": 86400, "needs_possession": true }, "pending_rotations": [] и "fees": { "mint_bps": 10, "burn_bps": 10, "recipient": "rand1…" }. Цепочка без раздела bridge отвечает {"enabled": false}.
Балансов нет: ценность, переданная через мост, — это ноты, и кошелёк считывает то, чем владеет, командой rand asset-balance [INDEX] по собственным нотам. Корень моста фиксирует эмиттеры, наборы хранителей, потраченные дайджесты, последовательность сжиганий, постквантовый набор хранителей, а также ключ паузы, флаг паузы и nonce управления (два, плюс rotation_nonce при правилах v2), нижнюю границу воспроизведения, если генезис её задаёт, а в цепочке 20 — правила ротации с любой отложенной ротацией и группу комиссий; он входит в корень состояния только в цепочке, где есть мост. Реестр токенов с locked каждого обеспечения фиксируется отдельно, как tokens_root. GET /bridge и GET /bridge/assets обозревателя отдают то же состояние, второй — с итогами депозитов и сжиганий по каждому обеспечению в виде строк с 8 десятичными знаками; GET /tokens и GET /tokens/:id отдают реестр.
Что публично, а что скрыто
| что | кто видит |
|---|---|
| сумма депозита, индекс актива и адрес получателя | все, в этой одной транзакции |
| какую ноту получатель позже тратит | никто |
| сумма сжигания, актив, монета, назначение и комиссия релеера | все — хранителям это нужно |
| сумма, актив и владелец ноты комиссии моста, казначейство (цепочка 20) | все: она восстанавливается из публичных полей |
| какие ноты финансировали сжигание | никто |
| балансы, переданные через мост | только владелец или держатель ключа просмотра |
наборы хранителей, эмиттеры, реестр с locked каждого обеспечения, журнал сжиганий | все |
Известные пробелы
Из собственного списка fullnode: на Rand релееру ничего не платят; конверт депозита ни с чем не связан, поэтому враждебный релеер может запечатать мусор, и именно поэтому кошелёк восстанавливает ноты депозита из публичных полей; общие тестовые векторы уровня реестра не перестроены под пул нот; bridge-burn по-прежнему узнаёт о политике самой конечной точки назначения только после оплаты доказательства; временные метки блоков, равные родительским, допускаются в цепочке с мостом, поэтому сговорившиеся лидеры могли бы удерживать заменённый набор хранителей в пределах его льготного окна; журнал сжиганий в памяти не ограничен; light client или проверки состояния исходной цепочки в самой цепочке не существует и не планируется.