Use esta página para integrar com a ponte: criar um depósito em uma chain de origem, enviar uma atestação na Rand, queimar um ativo ponteado de volta e ler o estado público da ponte. A chain 20, a testnet atual (ativa desde 2026-10-01 06:17 UTC, fullnode v0.6.8), foi criada com uma seção bridge: oito guardiões a partir do índice de conjunto de guardiões 1, oito guardiões pós-quânticos, uma chave de pausa, emissores para Ethereum, BSC, Tron e Solana (os três primeiros, os endpoints reimplantados em 2026-09-30), o grupo rules-v2, cujo limite global de emissão é de 4.000 zUSD por 24 horas contínuas, um piso de replay de entrada e dois grupos novos na v0.6.8: taxas de 10 bps em cada emissão e em cada queima, cobradas em zUSD, e regras de rotação que obrigam uma rotação do conjunto pós-quântico ou da chave de pausa a provar a posse das novas chaves e a aguardar um dia. Ao contrário da chain 14, o seu genesis lista o zUSD com os sete lastros e carrega a custódia que desceu da chain 14 a cada corte desde então (Herdado da chain 14). rand_getAssets e o /bridge/assets do explorador informam o locked atual de cada lastro.
O fluxo
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 │
└────────────────────────────────┘
Entrada: o valor é bloqueado na chain de origem, os guardiões atestam, qualquer pessoa envia a atestação à Rand, e o ledger acrescenta uma nota de depósito para o destinatário, uma nota cuja palavra asset é o índice do token ponteado no registro. Saída: uma transação Rand queima notas desse ativo e emite uma mensagem; os guardiões atestam essa mensagem; o contrato da chain de origem libera os fundos. A Rand nunca verifica um cabeçalho nem uma prova de estado da chain de origem: as assinaturas do comitê de guardiões são todo o modelo de confiança.
Modelo de confiança e ids de chain
Ids de chain locais da ponte, compartilhados com os contratos (bridge-codec):
| chain | id |
|---|---|
| Rand | 1 |
| Ethereum | 2 |
| BSC | 3 |
| Tron | 4 |
| Solana | 5 |
Os guardiões formam um comitê de chaves secp256k1; a identidade de um guardião são os últimos 20 bytes de keccak256(uncompressed_pubkey[1..]), a derivação de endereço da Ethereum, de modo que uma só chave funciona nos contratos EVM, no programa Solana e na Rand. O quórum para um conjunto de tamanho n é n·2/3 + 1. Toda assinatura deve ser low-s (s ≤ n/2), o que impede a maleabilidade. O digest que os guardiões assinam é mu = keccak256(keccak256(body_bytes)). Uma rotação do conjunto de guardiões é uma atestação do emissor de governança (chain 1, keccak256("rand-bridge-governance")), deve trazer new_index == current + 1 e deve ser assinada pelo conjunto atual, não por um já substituído; o conjunto que sai mantém uma janela de carência de 86.400 segundos para transferências em andamento. (Esse é o conjunto ECDSA. O conjunto pós-quântico e a chave de pausa rotacionam por ações próprias, que na chain 20 aguardam um atraso de um dia: abaixo.) Se n·2/3 + 1 guardiões conspirarem, podem emitir valor arbitrário; o reforço da ponte limita quanto e com que rapidez, mas não elimina isso.
Vinculação da transação. Uma transação Rand não carrega assinatura: uma queima é autorizada pela prova STARK do seu bundle. Desde a chain 14, toda prova de bundle é feita sobre Transaction::binding, e verificada contra ele, um hash da transação inteira (id da chain, o único bundle com seus quatro envelopes, a ação) com apenas os bytes da prova zerados. Desde a chain 17, um bundle carrega duas provas, a prova do bundle e a prova de autorização de gasto: a vinculação zera ambas, mantém o auth_commit do bundle, e ambas são verificadas contra ela. Uma cópia de uma queima pendente com to, relayer_fee ou (to_chain, token) alterados, ou de uma atestação com time ou envelope alterados, falha na prova (TxError::InvalidBundleProof). Na chain 20, a vinculação também carrega o hash do genesis (binding_domain: 1 do genesis: rand-tx-bind-2 sobre o hash do genesis, o id da chain, o bundle e a ação), de modo que uma prova feita para outra chain que compartilhe o id da chain tampouco é verificada. Carteiras e relayers devem executar um binário compilado para a chain à qual enviam: v0.6.8 na chain 20 (v0.6.7 nas chains 18 e 19); um rand mais antigo produz provas e assinaturas que a chain 20 recusa.
Os daemons de guardião e de relayer não fazem parte do full node; o full node carrega o codec compartilhado e a metade receptora.
Formato de transmissão
Todo inteiro é big-endian; a versão do formato é 1.
Envelope de atestação
| offset | len | field |
|---|---|---|
| 0 | 1 | version = 1 |
| 1 | 4 | guardian_set_index |
| 5 | 1 | n_sigs |
| 6 | 66·n_sigs | assinaturas: index (1), r (32), s (32), v (1) |
6 + 66·n_sigs | … | body |
Cabeçalho do corpo — 51 bytes, depois o payload
| offset | len | field |
|---|---|---|
| 0 | 4 | timestamp |
| 4 | 4 | nonce |
| 8 | 2 | emitter_chain |
| 10 | 32 | emitter_address |
| 42 | 8 | sequence |
| 50 | 1 | consistency_level |
| 51 | … | payload |
Payload Transfer — id 1, 133 bytes
| offset | len | field |
|---|---|---|
| 0 | 1 | id = 1 |
| 1 | 32 | amount (u256; a Rand usa os 128 bits inferiores, e um depósito também deve caber no u64 de uma nota) |
| 33 | 32 | token_address |
| 65 | 2 | token_chain |
| 67 | 32 | to — o hash do destinatário, veja abaixo |
| 99 | 2 | to_chain |
| 101 | 32 | fee (u256): a parcela do amount destinada ao relayer |
Payload GuardianSetUpgrade — id 2: id (1), new_index (4), n (1), depois n chaves de 20 bytes.
Os endereços Ethereum, BSC e Tron têm 20 bytes preenchidos à esquerda com 12 bytes zero nos campos de 32 bytes; um endereço Solana ocupa o campo inteiro. Um Transfer deve vir do emissor registrado da chain para emitter_chain, e token_chain deve ser igual à chain emissora. Uma atestação tem no máximo 16 KiB. Nada do reforço da ponte alterou o formato de transmissão, o codec nem os endpoints de origem implantados: as co-assinaturas pós-quânticas e as mensagens de governança viajam na transação Rand, não na atestação.
O destinatário de 32 bytes
Um endereço blindado tem 1.216 bytes e o formato de transmissão tem espaço para 32. O campo to carrega um hash dele:
to = blake3("rand-shielded-recipient" || pk_bytes || kem_ek) // ShieldedAddress::recipient_hash
O depositante informa o hash na chain de origem, os guardiões o assinam, quem submete coloca o endereço completo na ação BridgeAttest, e o ledger recalcula o hash e recusa uma divergência; de outro modo, quem submete poderia escolher quem recebe o depósito de outra pessoa. Os front ends da chain de origem devem aceitar essa forma; a carteira a imprime. A chain 11 fez brevemente do campo o id do receptor diretamente; essa forma de endereço foi revertida com a chain 12 (Contas e endereços).
Um token, vários lastros: zUSD
O registro de ativos deixou de ser da ponte. Ele passou para o registro de tokens RPL no nível do ledger, compartilhado por tokens nativos e ponteados, e a raiz de estado da chain se compromete com ele como tokens_root. Um token ponteado é uma entrada do registro cuja autoridade de emissão é Bridge { backings }, e ele já não corresponde a um único par (chain, token). O primeiro, o zUSD, é lastreado por USDT e USDC bloqueados em quatro chains — Ethereum, Binance Smart Chain e Solana carregam as duas moedas, Tron apenas USDT (a Circle descontinuou o USDC na 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
Na chain 20, o zUSD é o índice 1, rpl1xtj6k2x8strx8c2d5fjs50ltztck5ykms4ve7nmzmstf6fhn0v0spelqtx, nome “Shielded USD”, 8 casas decimais, listado no arquivo de genesis, como nas chains 15 a 19, com o nome, o símbolo e o salt que tinha na chain 14, de modo que o seu id de ativo (32e5ab28c782c663e14da2650a3feb12f16a12db85599f4f62dc169d26f37b1f) e o id rpl1… foram herdados sem alteração (explorador: GET /tokens/1). Na chain 14, ele foi registrado na altura 256 por uma transação register_bridged_token.
Backing { chain: u16, token: [u8; 32], decimals: u8, locked: u64, minted_today: u64, mint_day: u32 }
Um par (chain, token) lastreia no máximo um token em todo o registro (TokenError::BackingTaken); um token tem de 1 a 32 lastros; decimals é a contagem do próprio token de origem, de 0 a 18, nunca as 8 do token ponteado. O lastro agrupado é um risco declarado: as sete moedas são fungíveis com o zUSD, na entrada e na saída, ao par, de modo que um depeg de qualquer uma delas dilui todos os detentores de zUSD, e não há na Rand um limite para a participação de um lastro.
zUSD é uma cesta. Quem detém zUSD assume o risco de cada lastro listado, e não apenas o da moeda que depositou: um zUSD é um direito sobre o pool, exercível contra qualquer lastro com locked suficiente. Nada precifica um lastro. O bloqueio de 1 unidade de qualquer moeda listada cunha 1 zUSD, e uma queima pode nomear qualquer lastro cujo locked a cubra; assim, uma moeda que perdeu a paridade pode ser depositada ao par e o zUSD queimado em direção a uma moeda que manteve a paridade, até que os lastros sólidos sejam esgotados. A perda é limitada pelas janelas de cunhagem móveis e pela custódia efetivamente bloqueada: na chain 20, no máximo 4.000 zUSD em todos os lastros, e 100.000 por lastro, podem ser cunhados em qualquer período de 24 horas (rules v2), e não pode sair por um lastro sólido mais do que esse lastro mantém. A janela limita a taxa, não o total: ela compra o tempo para pausar as cunhagens, o que impede novos depósitos da moeda fraca enquanto as queimas continuam abertas. Não há limite de resgate por lastro nem pausa por lastro. Na chain 14, antes das rules v2, o pauser próprio dos endpoints de origem e os limites de taxa por token eram o único limite.
A invariante: total_supply == Σ backings.locked. TokenRegistry::lock (uma BridgeAttest) soma o valor atestado ao locked do lastro e ao total_supply do token em uma única chamada; TokenRegistry::release (uma BridgeBurn) subtrai o valor liberado de ambos. Nada mais escreve em nenhum dos dois campos, e a folha do token compromete o locked de cada lastro em tokens_root, de modo que dois nós que divergem sobre um deles divergem na raiz de estado. Os endpoints de origem não cobram taxa no bloqueio e deduzem o valor atestado inteiro na liberação, de modo que o locked de cada lastro é igual à custódia desse endpoint, salvo mensagens em trânsito. As taxas na chain 20 não quebram isso: uma taxa é uma note de zUSD que permanece emitida e lastreada, de modo que o depósito ainda bloqueia o valor bruto e a queima libera apenas amount − fee. Na chain 20, o desconto próprio de 10 bps de USDT dos endpoints na liberação foi definido como 0 no corte; a taxa agora é cobrada na Rand.
Os valores são unidades de 8 casas decimais na rede, quaisquer que sejam as casas decimais da própria moeda de origem: um depósito de 1 USDT vindo da Ethereum (6 casas decimais) é bloqueado como 100_000_000 unidades. Somente o índice 0, RAND, tem as nove casas decimais desta chain.
Um token precisa estar listado antes do seu primeiro depósito. Não existe mais registro no primeiro avistamento: uma atestação que nomeia um (chain, token) que ninguém listou é recusada com BridgeError::UnlistedToken (token … of chain … is not listed on this chain). Os fundos permanecem na custódia do endpoint de origem até que um quórum de guardiões pós-quânticos liste a moeda (Listing after genesis).
asset_id = blake3("rand-bridge-asset" || token_chain (2 bytes BE) || token_address (32 bytes))
rand_bridgeAssetId(token_chain, token_address) ainda calcula isso em qualquer chain, e é nisso que o (token_chain, token_address) de um payload de Transfer se resolve, mas deixou de ser uma chave do registro. O que uma note e uma carteira carregam é o índice do token, um u32 denso: o índice 0 é RAND e nunca está no registro. rand_getAssets tem uma linha por lastro, todas as linhas de um mesmo token sob o mesmo índice:
[{ "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 e minted_today são strings decimais em unidades de 8 casas decimais, como todo valor u64 que o RPC serve desde a chain 14; index, chain, decimals e mint_day continuam sendo números. Os métodos no nível do token — rand_getTokens, rand_getToken, rand_getTokenSupply — retornam um token com bridge como uma linha que carrega todos os seus lastros (RPL tokens). O GET /bridge/tokens do explorador é a lista de permissões estática das moedas que a bridge aceita.
Inbound: 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
Transportada por uma transação blindada comum, cujo único pacote de ativo oculto paga a base de 0,001 RAND e nada além disso: seu relayer está pagando por um depositante que ainda não tem RAND. A chain, e não quem submete, calcula a note de depósito { pk: recipient.pk, from: 0, amount, asset: <index>, time, r } a partir do valor que os guardiões assinaram, de modo que um relayer não pode inflar nem redirecionar uma cunhagem; o seu compromisso é a única folha que uma transação não carrega na rede. Na chain 20, o amount da note é o valor bruto menos a taxa de cunhagem, e a chain acrescenta uma segunda note, a taxa, ao tesouro (Fees on mint and burn).
O blinding é derivado, não escolhido. r deve ser
r = blake3("rand-deposit-r-1" || mu) // read as eight little-endian words; mu = keccak256(keccak256(body))
o digest que o quórum de guardiões assinou; qualquer outra coisa é BridgeError::WrongDepositBlinding, recusada antes de qualquer trabalho de assinatura. O campo permanece na rede — a reconstrução de campos públicos da carteira o lê ali — ele apenas é restringido, e a regra cobre também a attest de uma rotação. Antes disso, qualquer pessoa que tivesse visto a transação pendente de um relayer podia submeter a mesma atestação com um blinding próprio: o destinatário ainda recebia uma note, mas a transação do relayer morria depois de ele ter pago por uma prova. Com r fixado, dois que submetam uma mesma atestação em um mesmo time nomeiam uma só note, e o pool os conflita pelo compromisso.
time e asset são as duas previsões do depositante, feitas para que o envelope possa ser selado antes de a transação existir. time é mantido na mesma janela de um pacote (1.024 blocos na chain 20, a partir de proof_window_blocks do genesis; 256 antes), e deliberadamente não é derivado: nenhuma regra dá um time com o qual todos os que submetem concordariam, de modo que um front-runner ainda pode escolher outro time na janela — uma note diferente do mesmo destinatário, valor e ativo, que a carteira do destinatário encontra da mesma forma — e as duas submissões colidem no digest da atestação. asset deve ser igual ao índice que o registro já deu a (chain, token), caso contrário TxError::AttestAssetMismatch.
O fee do payload de Transfer é carregado e não é pago a ninguém: não há identidade de quem submete a pagar, de modo que o depósito é o valor bruto, menos apenas a taxa de cunhagem da própria chain onde o genesis define uma (chain 20). A proteção contra replay é por digest: todo mu consumido entra no conjunto spent da bridge, e a mempool recusa um segundo relayer que dispute a mesma atestação. Um depósito também é limitado pelo limite de cunhagem do lastro, na chain 20 também pelo limite global, e é recusado enquanto as cunhagens estão pausadas (B1, rules v2). Ambos os limites contam o valor bruto. Em uma chain cujo genesis define um piso de replay, a chain 20 inclusive, uma transferência cujo sequence esteja abaixo do piso de sua chain de origem é recusada com BelowReplayFloor antes de qualquer assinatura ser verificada (Carried over from 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 é obrigatório: o quórum de co-assinaturas Dilithium2 dos guardiões como [{"index":0,"signature":"<4840 hex>"},…]. Antes de qualquer prova, a carteira decodifica a atestação, verifica o quórum contra o pq_guardians do nó e o id da chain, verifica que o destinatário gera o hash do atestado e lê o índice listado; em seguida deriva r, sela o envelope do destinatário (pelo valor líquido em uma chain com taxas) e paga com um pacote próprio de RAND. bridge-rotate submete uma rotação do conjunto de guardiões (payload 2), que não deposita nada, e recusa tudo o que não avance o conjunto da Rand em um passo. Como todo campo de uma note de depósito é público naquela única transação, a carteira de um destinatário reconstrói as notes de depósito endereçadas a ela percorrendo as transações bridge_attest confirmadas, sem abrir nenhum envelope — um relayer hostil pode selar lixo, mas não pode esconder a note.
Outbound: 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 }
}
Desde o pacote de ativo oculto da chain 14, uma queima é um pacote (antes eram dois): os slots 0–1 provam que quem queima possuía notes do ativo que somam ao menos amount, com burn_a o valor que sai do pool, e os slots 2–3 pagam a taxa em RAND. A taxa é BRIDGE_BURN_FEE, 0,01 RAND, que cobre a base do pacote. relayer_fee é uma parcela de amount (fee ≤ amount) separada na chain de destino pelo contrato de liberação, e não uma cobrança extra, de modo que burn_a é exatamente amount. Na chain 20, a chain também retém uma taxa de queima de 10 bps de amount como uma note de zUSD para o tesouro, e a liberação é amount − fee; a parcela do relayer deve caber dentro dessa liberação (Fees on mint and burn).
Uma queima nomeia a sua moeda. (to_chain, token) deve ser um lastro do ativo queimado (TokenError::NotABacking caso contrário), e duas regras são verificadas na etapa de ação, antes da prova do pacote:
- A unidade de liberação. Um lastro com
d < 8casas decimais de origem libera em unidades de10^(8−d)(100 para uma moeda de 6 casas decimais; 1 para uma de 18). O valor liberado e orelayer_feedevem ser, cada um, um múltiplo inteiro, caso contrárioTokenError::NotReleasable(… is not a multiple of the release unit …): uma fração abaixo da menor unidade da moeda de origem ficaria encalhada na custódia. - A custódia do próprio lastro. O valor liberado
≤ lockeddaquele lastro, caso contrárioTokenError::InsufficientBacking(only … is locked in that backing, not the … asked for). O fornecimento total do token pode cobrir a liberação; o que precisa cobri-la é a custódia deste único lastro.
O valor liberado é amount em uma chain sem taxas e amount − fee na chain 20.
to deve ter o formato correto (20 bytes preenchidos com zeros à esquerda nas chains 2, 3 e 4; nunca todos zero). O ledger registra um BridgeBurnRecord sob um burn_sequence estritamente crescente, com o hash da transação no slot do remetente, e os guardiões leem o log de queimas:
→ {"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 é a mensagem de saída exatamente como os guardiões devem calculá-la (hash) e assiná-la; o valor de Transfer é release_amount. O registro acima é o primeiro burn da chain 20: 0,999 zUSD queimados rumo a USDT na BSC na altura 2.899, dos quais 0,000999 foram para a tesouraria e 0,998001 foram liberados.
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
A carteira verifica primeiro — a partir de uma única leitura do estado da bridge e do registro de tokens, antes de qualquer prova — que a chain tem uma bridge, que ASSET está no registro, que TOKEN em TO_CHAIN o lastreia e tem pelo menos AMOUNT bloqueado, e que AMOUNT e a taxa do relayer são unidades inteiras de liberação. Em uma chain com taxas, ela então imprime a taxa da bridge e o valor que será liberado, e pergunta antes de provar (--yes dispensa a pergunta). Um burn então prova um único bundle: cerca de 100 s em um laptop e cerca de 1,5 MB de prova e, na chain 20, como em todo bundle desde a chain 17, uma prova de autorização de gasto ao lado (alguns segundos, cerca de 1,37 MB).
Taxas sobre mint e burn
Novidade no fullnode v0.6.8 e definido na chain 20 pelo grupo de gênese
"bridge": { …, "fees": { "mint_bps": 10, "burn_bps": 10, "recipient": "rand1…" } }
a chain cobra uma taxa sobre cada mint e cada burn via bridge, no token da bridge, na Rand, como uma nota shielded para o endereço de tesouraria recipient. Cada alíquota é de no máximo 100 bps (1 %); na chain 20, ambas são de 10 bps, 0,1 %. A taxa é ⌊amount · bps / 10⁴⌋ arredondada para baixo até uma unidade inteira de liberação do lastro (10^(8 − source decimals): 100 unidades para um USDT de 6 decimais, 1 para uma moeda de 18 decimais), de modo que a taxa e o restante são ambos unidades inteiras, e uma taxa que arredonda para zero não gera nota (ledger/bridge_notes.rs, bridge_fee).
depósito (BridgeAttest) de bruto g | burn (BridgeBurn) de amount | |
|---|---|---|
| taxa | ⌊g · mint_bps / 10⁴⌋, para baixo até uma unidade de liberação | ⌊amount · burn_bps / 10⁴⌋, para baixo até uma unidade de liberação |
| o lado do usuário | uma nota de depósito de g − fee | amount queimado (burn_a == amount, sem troco do guest); o corpo de saída atesta release_amount = amount − fee, e o endpoint libera exatamente esse valor |
| a nota da tesouraria | fee, blinding blake3("rand-bridge-fee-r-1" ‖ mu), no time e no ativo do depósito | fee, blinding blake3("rand-bridge-burn-fee-r-1" ‖ txid), no time do bundle |
locked e total_supply | aumentam em g | diminuem em release_amount |
| verificado contra | os tetos de mint por lastro e global, sobre g | InsufficientBacking e NotReleasable sobre release_amount; a parcela do relayer deve caber nele (FeeExceedsAmount) |
Ambas as notas são emitidas e lastreadas, de modo que total_supply == Σ locked == custody continua válido: a nota de taxa é zUSD como qualquer outra, e seu valor permanece bloqueado na chain de origem até que alguém a queime. BRIDGE_BURN_FEE, os 0,01 RAND que um burn paga em seus slots de RAND, não muda e é separado.
Primeira viagem de ida e volta da chain 20, lida do nó: um depósito de 1 zUSD vindo da BSC na altura 1.664 (4f6e6c54…1a64) deu ao depositante 0,999 zUSD e à tesouraria 0,001; um burn de 0,999 zUSD de volta para USDT na BSC na altura 2.899 (sequência 13) reteve 0,000999 zUSD e liberou 0,998001. Depois dele (lido em 2026-10-01), o lastro de USDT na BSC mantinha 22,001999 zUSD, e a oferta do token, 99,001999 zUSD, era igual à soma de seus lastros.
Como a tesouraria encontra suas notas. Uma nota de taxa não tem envelope: cada palavra dela é pública (o valor vem da transação e da alíquota de gênese, o ativo e o time vêm da transação, o blinding é derivado, o dono é o recipient de gênese). A carteira cujo endereço é recipient reconstrói as notas de taxa na mesma passagem pública que reconstrói as notas de depósito, lendo rand_getBridgeState.fees e os decimais dos lastros uma vez por sincronização. Um nó que minta sobre as taxas só pode fazer uma nota não ser encontrada, nunca creditar uma. A carteira de um depositante avalia o próprio depósito da mesma forma, bruto menos a taxa. Só uma carteira v0.6.8 faz isso; uma mais antiga não consegue enviar na chain 20 de qualquer modo.
Sem o grupo, nas chains 14 a 19, não há taxa: um depósito credita o valor bruto e um burn libera amount. Até a chain 20, a única taxa da bridge era o desconto de 10 bps de USDT que os próprios endpoints de origem cobravam na liberação, na chain anfitriã; ela foi definida como 0 no corte da chain 20. O grupo é comprometido com o hash de gênese e incorporado à raiz da bridge sob rand-bridge-fees-1. O docs/bridge.md §25 do fullnode traz a codificação.
Fortalecimento da bridge
Os locked e InsufficientBacking por lastro protegem o resgate. Eles não limitam a emissão: uma atestação com um quórum de guardiões válido emite o que ela diz. Três camadas fecham essa lacuna e uma quarta permite listar um token sem um corte de chain. Nenhuma altera os endpoints da chain de origem, o formato de transmissão da atestação nem o bridge-codec.
B1: um teto de mint por lastro e uma pausa de mint
O tokens.mint_cap_per_day de gênese se aplica por lastro: a chain 20 define 10000000000000, 100.000 zUSD por lastro por dia, como toda chain desde a 14. Sem as rules v2 (chain 14), um dia é timestamp_ms / 86_400_000 do timestamp do bloco; sob as rules v2 (chains 15 a 20), o mesmo teto é medido sobre uma janela móvel de cap_window_secs de tempo de bloco, e o teto global de todo o registro — 4.000 zUSD por 86.400 s na chain 20 — é o limite mais restritivo (rules v2). Um depósito acima do teto é recusado com TokenError::MintCapExceeded antes do trabalho de assinatura dos guardiões, e é admissível assim que a janela volta a ter espaço.
O bridge.pause_key de gênese é uma única chave Dilithium2, distinta de todo guardião pós-quântico. Duas ações sem bundle e sem taxa:
PauseMints { nonce, signature }— assinada apenas pela chave de pausa. Ela só pode pausar.UnpauseMints { nonce, pq_signatures }— exige um quórum de guardiões pós-quânticos, nunca a chave de pausa.
Ambas carregam e incrementam pause_nonce, de modo que nenhuma pode ser reaplicada. Enquanto pausado, todo BridgeAttest de transferência é recusado com BridgeError::MintsPaused; burns e rotações do conjunto de guardiões continuam abertos, de modo que uma pausa nunca prende o resgate. Sob as rules v2 (chains 15 a 20), a listagem também é recusada enquanto pausado. A chave de pausa deve ficar em uma máquina diferente das chaves dos guardiões.
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]
O que a pausa cobre: daemons de guardiões enganados ou comprometidos a co-assinar um mint forjado enquanto os detentores do quórum pós-quântico não o são. Ela não é uma defesa contra o roubo desse quórum, que pode retirar a pausa, listar até 32 lastros em um token, cada um com seu próprio teto diário, e, sob as rules v2, substituir o conjunto pós-quântico e a chave de pausa.
B2: timestamps de bloco limitados
O timestamp de bloco de uma chain com bridge decide a expiração do conjunto de guardiões e o dia do teto de mint, de modo que um líder não pode ter a possibilidade de saltá-lo. Em uma chain com bridge:
- Uma regra de validade, verificada também na reexecução:
timestamp_ms ≤ parent.timestamp_ms + 60_000, senãoBlockError::TimestampLeap. Com a regra de retrocesso existente (BlockError::TimestampRewind), o tempo de bloco só avança, no máximo um minuto por bloco. - Uma regra de voto, nunca uma regra de reexecução: um validador retém seu voto em um bloco com mais de 15.000 ms à frente do seu próprio relógio.
Todo validador em uma chain com bridge precisa, portanto, de NTP: um com mais de 15 segundos de desvio é um líder defeituoso e um votante defeituoso. Enquanto uma chain se recupera após uma parada, o tempo de bloco pode avançar até 60 s por bloco, de modo que um dia de teto de mint pode virar mais rápido que um dia real — no máximo o teto de um dia extra por dia completo de atraso acumulado.
B3: uma co-assinatura Dilithium2 em cada mint
Todo BridgeAttest, tanto um depósito quanto uma rotação, deve levar, ao lado de seu quórum ECDSA, um quórum de assinaturas Dilithium2 sobre
M = "rand-bridge-pq-cosign-1" || rand_chain_id (u64 BE) || mu // 63 bytes
mu é o digest que o quórum ECDSA assina; o chain id impede que uma co-assinatura de testnet seja verificada em outra chain Rand. As assinaturas vão no pq_signatures da ação, dentro do vínculo da transação, de modo que não podem ser removidas nem trocadas em uma cópia. Verificado o barato antes do caro: quorum(n) ≤ len ≤ n (PqNoQuorum), índices estritamente crescentes (PqIndexOrder), todo índice < n (PqIndexOutOfRange), toda assinatura com exatamente 2.420 bytes (PqBadSignatureLength) e, por último, que cada uma é verificada sob pq_guardians[index] (PqBadSignature). Os dois quóruns são contados de forma independente. O bridge.pq_guardians de gênese está alinhado por índice ao conjunto ECDSA de gênese (conjunto 1 na chain 20). Uma rotação de payload 2 move apenas o conjunto ECDSA; sem as rules v2 o conjunto pós-quântico é fixo durante toda a vida da chain, e sob as rules v2 apenas RotatePqGuardians o substitui (na chain 20, sua forma V2, com uma prova de posse e um atraso de um dia). A chain 20 tem oito, as mesmas oito chaves das chains 15 a 19; um mint precisa de seis delas (a chain 14 tinha seis e precisava de cinco).
Esta co-assinatura continua vinculando apenas o chain id, também na chain 20: ao contrário das mensagens de governança abaixo, ela não foi movida para trás do genesis hash, porque os daemons de guardiões que a produzem estão em outro repositório e não mudaram. Uma cópia de uma chain com o mesmo id, que tenha copiado também o conjunto de guardiões, aceitaria as co-assinaturas de um depósito feitas para a original; o fee bundle da própria transação de depósito está vinculado ao genesis na chain 20, mas a attestation contida nele não está.
B4: listar um token com bridge após o genesis
Um endereço comum nunca pode criar um token com autoridade Bridge. Duas ações, cada uma com um fee bundle comum em RAND e cada uma sob o quórum pós-quântico:
RegisterBridgedToken { name, symbol, salt, chain, token, decimals, nonce, pq_signatures }— um novo token com bridge no próximo índice, com 8 casas decimais na Rand, sob o limite de emissão do genesis, com seu primeiro lastro. Taxa: a base do bundle mais aregistration_feedo registro (1 RAND na chain 20, ou seja, 1,001 RAND; a chain 20 queima a taxa de registro em vez de pagá-la ao proposer).ListBacking { token_index, chain, token, decimals, nonce, pq_signatures }— um lastro adicional em um token com bridge já registrado, começando comlocked = 0. Taxa: a base do bundle.
Ambas carregam e incrementam list_nonce (caso contrário, BadListNonce { expected, got }) e executam as mesmas verificações: a chain tem um emissor registrado (BridgeError::NoEmitter), (chain, token) ainda não lastreia nada, menos de 32 lastros, 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>
Duas regras de ordenação. Os guardiões assinam primeiro o registro, esperam que ele seja confirmado, leem de volta o índice atribuído e só então assinam as listagens que o citam — registrar um token nativo não exige permissão, de modo que uma listagem assinada antecipadamente poderia encontrar seu índice já ocupado. E uma moeda é listada na Rand antes de setToken habilitá-la no endpoint de origem: no sentido inverso, um depósito poderia bloquear fundos antes de a Rand ter um lastro a creditar, e sua attestation seria recusada com UnlistedToken até que a Rand o alcançasse.
Mensagens de governança
Bytes fixos, nunca bincode; todos os inteiros em 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 carregam pause_nonce, M_list/M_register carregam list_nonce, e M_rotate_pq/M_rotate_pause carregam rotation_nonce. count é o número de chaves, não um comprimento em bytes; cada chave é a chave pública Dilithium2 em seus 1.312 bytes brutos, na ordem que o novo conjunto terá.
Na chain 20, cada mensagem está vinculada ao genesis hash (genesis binding_domain: 1): o dígito de versão da tag passa a 2 e o genesis hash de 32 bytes fica entre a tag e o chain id, com o restante inalterado — M'_pause = "rand-bridge-pause-2" || genesis [32] || chain_id u64 || nonce u64, e do mesmo modo rand-bridge-pq-unpause-2, rand-bridge-pq-list-2, rand-bridge-pq-register-2, rand-bridge-pq-rotate-pq-2 e rand-bridge-pq-rotate-pause-2 (bridge/gov.rs). Uma chave de pausa ou um quórum que assina para uma chain não assina então para nenhuma outra chain que compartilhe seu chain id e suas chaves. Um arquivo assinado na forma …-1 é recusado na chain 20 (BadPauseSignature, PqBadSignature). As chains 14 a 19 usam os layouts …-1 acima, byte a byte.
Rules v2: rotações, limites em janela móvel, sem listagem durante a pausa
Um grupo opcional dentro da seção bridge do genesis. A chain 14 não o tinha; toda chain desde a 15 o tem, e a chain 20 com os mesmos valores:
"bridge": { …, "rules_v2": { "global_mint_cap_per_window": 400000000000, "cap_window_secs": 86400 } }
- Duas rotações sob o quórum pós-quântico.
RotatePqGuardians { new_pq_guardians, nonce, pq_signatures }substitui todo o conjunto pós-quântico;RotatePauseKey { new_pause_key, nonce, pq_signatures }substitui a chave de pausa. Ambas dispensam bundle e taxa, comoPauseMints, ambas são assinadas pelo conjunto pós-quântico atual, e ambas carregam e incrementamrotation_nonce, de modo que um quórum assinado autoriza exatamente uma rotação. O novo conjunto deve ter o mesmo tamanho do conjunto ECDSA atual, chaves de 1.312 bytes, sem repetições e sem a chave de pausa. Não há janela de tolerância: o conjunto ou a chave substituídos não assinam mais nada. Na chain 20, essas ações v1 são recusadas, e uma rotação deve provar a posse das novas chaves e aguardar um dia (abaixo). - Limites em janela móvel. O
mint_cap_per_dayde cada lastro é medido sobre uma janela móvel decap_window_secsem vez do dia civil, e um segundo limite, válido para todo o registro,global_mint_cap_per_window, cobre em conjunto todos os lastros de todos os tokens (TokenError::GlobalMintCapExceeded). Na chain 20, são 400.000.000.000 unidades, 4.000 zUSD por 86.400 s. Pela regra do dia, o limite às 23:59:59 e novamente às 00:00:01 equivalia ao dobro do limite em dois segundos; pela janela, o segundo é recusado. - Sem listagem durante a pausa.
RegisterBridgedTokeneListBackingsão recusados comMintsPausedenquantomint_paused. Os burns e as duas rotações permanecem abertos.
Rotação: posse e um dia de atraso
As Rules v2 deixaram três questões em aberto: nada provava que as novas chaves eram mantidas por alguém (um erro de digitação, ou uma chave que ninguém possui, inutilizaria o quórum pós-quântico para sempre), uma rotação entrava em vigor no bloco em que era incluída, e nada podia deter uma rotação hostil. Um segundo grupo opcional ao lado de rules_v2 (que ele exige), novo na v0.6.8 (audit v6, BRG-14), resolve as três. A chain 20 o define:
"bridge": { …, "rotation": { "delay_secs": 86400, "needs_possession": true } }
- Posse. Uma rotação deve ser
RotatePqGuardiansV2 { new_pq_guardians, possession, nonce, pq_signatures }ouRotatePauseKeyV2 { new_pause_key, possession, nonce, pq_signatures }:possession[i]é a assinatura Dilithium2 própria da nova chaveisobre a mesma mensagem que o quórum assina (M_rotate_pqouM_rotate_pause, na forma…-2na chain 20), uma por chave, na ordem das chaves, com 2.420 bytes cada. As ações v1 são recusadas comPossessionRequired. Uma contagem ou um comprimento incorretos são recusados antes de o quórum ser verificado; uma assinatura de posse inválida (BadPossession { index }), depois dele. - Um atraso. Uma rotação aceita é registrada como pendente, com
effective_at_secs = block time + delay_secs, e entra em vigor ao fim do primeiro bloco cujo timestamp o alcance. Na chain 20, são 86.400 s, um dia. Até lá, o conjunto antigo co-assina cada emissão e a chave de pausa antiga pausa. Aceitar uma rotação consome umrotation_noncee ativá-la consome outro, de modo que uma attestation de emissão reunida no pool sob o conjunto antigo sai do pool quando o conjunto muda. Pode haver uma rotação pendente de cada tipo por vez (RotationPending { effective_at_secs }). - O cancelamento.
CancelRotation { kind, nonce, signature }(sem bundle e sem taxa) descarta a rotação pendente dekind(0 = o conjunto pós-quântico, 1 = a chave de pausa). É assinado pela chave de pausa atual, a única chave mantida separada do quórum, sobre"rand-bridge-cancel-rotation-2" || genesis [32] || chain_id u64 || rotation_nonce u64 || kind u8na chain 20, e consome o nonce. Um quórum capturado não consegue, portanto, rotacionar o conjunto em menos de um dia, e o titular da chave de pausa pode detê-lo nesse dia; a chave de pausa entrante não pode cancelar a própria chegada.
O signatário é rand-node bridge-gov (rotate-pq, rotate-pause, cancel-rotation, cada um com as etapas message, sign e submit; submit reverifica cada linha de posse antes de enviar). rand_getBridgeState serve rotation_rules e pending_rotations; ambos são null em uma chain sem o grupo (chains 14 a 19). A rotação do conjunto de guardiões ECDSA (payload 2) não é afetada: mantém suas próprias regras e sua janela de tolerância. Nenhuma rotação foi executada na chain 20.
Herdado da chain 14
A chain 15 foi criada a partir da posição de bridge da chain 14 (os mesmos emissores e endpoints de origem), e as chains 16, 17 e 18 foram criadas, cada uma, da mesma forma a partir da sua antecessora. A chain 19 foi criada a partir da posição da chain 18 com uma única mudança: seus emissores de Ethereum, BSC e Tron são os endpoints reimplantados em 2026-09-30 (0x7af6b17047c1db6cb54347fdea45cf9179075bfa para as chains 2 e 3, 0x6410797df959987a5baf65b5fab97edeb34d5163 para a chain 4). A chain 18 nunca confiou neles, portanto nada deles foi emitido ali. A chain 20 foi criada a partir de um snapshot da chain 19 na altura 7.258, com a bridge parada antes, e mantém inalterados os emissores, guardiões, chaves pós-quânticas e chave de pausa da chain 19; o que ela acrescenta são os grupos de taxas e de rotação. O emissor da Solana e o emissor do lado da Rand não mudaram desde a chain 14. O que o genesis da chain 20 contém:
- Custódia. 99 zUSD, como duas notes zUSD de gênese (10 e 89 zUSD), contra 22 USDT bloqueados na Ethereum, 22 USDT na BSC, 30 USDT na Tron e 25 USDT na Solana, e 0 em todos os lastros em USDC: o
lockedda chain 19 no instantâneo, que coincidia com a custódia dos endpoints.total_supply == Σ lockedé igual à custódia dos endpoints no bloco 0. Histórico: os 10 zUSD que ficaram na chain 14 eram uma única note de gênese nas chains 15 a 19; a gênese da chain 18 os continha como 9 USDT bloqueados na Tron e 1 na Solana, e a da chain 19 como 10 na Solana, após um rebalanceamento na chain 18. Os outros 89 são os depósitos líquidos da chain 19, menos suas queimas. - Índice 1 do conjunto de guardiões, onde os endpoints de origem já estão; a próxima rotação ECDSA deve carregar o índice 2. As oito chaves pós-quânticas e a chave de pausa são as da chain 15, sem alteração.
burn_sequence13: as queimas da chain 14 terminaram em 6, a gênese das chains 15 a 18 começou em 7, a chain 18 fez uma queima (7) e a chain 19 começou em 8. A chain 19 fez cinco queimas, sequências 8 a 12, de modo que a chain 20 começa em 13, e nenhum endpoint ou daemon indexado por sequência vê uma delas duas vezes. A primeira queima da chain 20 recebeu a sequência 13.- Repetição (replay). Um mint de uma chain anterior ainda passaria no quórum ECDSA, mas sua co-assinatura pós-quântica vincula o id dessa chain, de modo que é recusado na chain 20. A gênese da chain 15 não tinha piso de repetição de entrada (
min_inbound_sequenceeranull), portanto ali isso dependia da co-assinatura e de os guardiões nunca co-assinarem um lock antigo para o novo id de chain. Desde a chain 16, a gênese define um: por chain de origem, a menorsequenceque uma atestação de transferência do emissor dessa chain pode ter. O da chain 20 é{ "2": 3, "3": 3, "4": 3, "5": 7 }: a próxima sequência de saída de cada endpoint quando a chain 19 foi capturada em instantâneo, tendo todo lock anterior sido cunhado na chain 19. (O da chain 18 era{ "2": 2, "3": 2, "4": 2, "5": 2 }; o da chain 19 era{ "2": 1, "3": 1, "4": 1, "5": 4 }, em que a sequência 0 de cada endpoint reimplantado era o lock do próprio operador, que nunca deve cunhar.) Uma transferência abaixo do piso é recusada comBelowReplayFloor, qualquer que seja o quórum e a co-assinatura que traga. As rotações do conjunto de guardiões não são julgadas por ele. O piso é fixado na gênese, comprometido no hash da gênese e incorporado à raiz da bridge.
Ordem de lançamento na chain 14 (histórico)
Como a bridge entrou no ar na chain 14, uma testnet anterior, em setembro de 2026:
- Cortar a chain com a seção
bridge(incluindopq_guardiansepause_key) e uma seçãotokensque defineregistration_feeemint_cap_per_day, sem listar nenhum token. Feito em 2026-09-20. - Habilitar o faucet para que uma carteira de implantação possa obter RAND. Feito.
- O implantador registra o zUSD com seu primeiro lastro, sob a assinatura do quórum sobre
M_registeremlist_nonce = 0. Feito na altura 256. - Os guardiões leem de volta o índice e assinam as seis mensagens
ListBackingrestantes, enviadas uma de cada vez. Feito:list_noncemarca 7. - Para cada lastro: listar na Rand antes de
setTokenno endpoint de origem. - A rotação do conjunto de guardiões (conjunto 0 → conjunto 1, assinada por 5 de 6 do conjunto atual) nos quatro endpoints de origem e, em seguida, a mesma atestação na Rand. A Rand marcou o conjunto 0 durante a vida da chain 14; a chain 15 começa no conjunto 1.
- Uma viagem de ida e volta ao vivo por chain de origem, com valores pequenos — bridge de entrada, mint de zUSD, transferência, queima de volta — e uma reconciliação do
lockedde cada lastro (rand_getTokenSupply) com a custódia do endpoint.
Estado público
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 são chaves públicas Dilithium2, de 1.312 bytes cada, em hexadecimal. Não existe mais next_index: um token com bridge é listado, sob um índice que seu registro fixou, antes de poder ser depositado, de modo que não resta índice algum a prever. rotation_nonce é o que o próximo M_rotate_pq/M_rotate_pause/M_cancel deve carregar; rules_v2 é null em uma chain sem o grupo (chain 14), e min_inbound_sequence é null em uma chain sem piso de repetição (chain 15 e anteriores); na chain 20 é { "2": 3, "3": 3, "4": 3, "5": 7 }. rotation_rules, pending_rotations e fees são null em uma chain sem esses grupos (chains 14 a 19). O exemplo acima mostra o formato, não os valores da chain 20: a chain 20 começou em guardian_set_index 1 e burn_sequence 13, e serve "rotation_rules": { "delay_secs": 86400, "needs_possession": true }, "pending_rotations": [] e "fees": { "mint_bps": 10, "burn_bps": 10, "recipient": "rand1…" }. Uma chain sem seção bridge responde {"enabled": false}.
Sem saldos: o valor com bridge são notes, e uma carteira lê o que possui com rand asset-balance [INDEX] a partir de suas próprias notes. A raiz da bridge compromete os emissores, os conjuntos de guardiões, os digests gastos, a sequência de queima, o conjunto de guardiões pós-quânticos e a chave de pausa, o sinalizador de pausa e os nonces de governança (dois, e rotation_nonce sob as regras v2), o piso de repetição quando a gênese define um e, na chain 20, as regras de rotação com qualquer rotação pendente e o grupo de taxas; ela é incorporada à raiz de estado somente em uma chain que tenha bridge. O registro de tokens, com o locked de cada lastro, é comprometido separadamente como tokens_root. O GET /bridge e o GET /bridge/assets do explorador servem o mesmo estado, o segundo com totais de depósito e queima por lastro como strings de 8 casas decimais; GET /tokens e GET /tokens/:id servem o registro.
O que é público e o que é oculto
| o quê | quem vê |
|---|---|
| o valor, o índice do ativo e o endereço do destinatário de um depósito | todos, nessa única transação |
| qual note um destinatário gasta depois | ninguém |
| o valor, o ativo, a coin, o destino e a taxa do relayer de uma queima | todos — os guardiões precisam disso |
| o valor, o ativo e o titular de uma note de taxa da bridge, a tesouraria (chain 20) | todos: ela é reconstruída a partir de campos públicos |
| quais notes financiaram uma queima | ninguém |
| saldos com bridge | somente o titular, ou um detentor de chave de visualização |
conjuntos de guardiões, emissores, o registro com o locked de cada lastro, o log de queimas | todos |
Lacunas conhecidas
Da própria lista do fullnode: nenhum relayer é pago na Rand; o envelope de um depósito não está vinculado a nada, de modo que um relayer hostil pode selar lixo, razão pela qual a carteira reconstrói as notes de depósito a partir de campos públicos; os vetores de teste compartilhados em nível de ledger não foram reconstruídos sobre o pool de notes; o bridge-burn ainda só descobre a política do próprio endpoint de destino depois de pagar por uma prova; timestamps de bloco iguais ao do pai são permitidos em uma chain com bridge, de modo que líderes em conluio poderiam manter um conjunto de guardiões substituído dentro de sua janela de carência; o log de queimas em memória é ilimitado; não existe, nem está planejado, cliente leve ou verificação on-chain do estado da chain de origem.