Use esta página para construir, enviar ou decodificar uma transação: o formato de transmissão que rand_sendTransaction recebe, como o id é calculado, a lista completa de ações com suas regras de pacote e de taxa, e a ordem de admissão que decide qual erro você recebe primeiro.
A transação
Transaction { chain_id: u64, bundle: Option<Bundle>, action: Action }
Bundle { // the hidden-asset bundle (since chain 14)
anchor: Word8, nullifiers: [Word8; 4], commitments: [Word8; 4],
fee: u64, burn_a: u64, burn_r: u64, burn_asset: u32, time: u32,
envelopes: [Envelope; 4], proof: Vec<u8>, // postcard(Proof) of the hidden-asset guest
auth_commit: Word8, auth_proof: Vec<u8>, // since chain 17: split authorisation's c and its auth proof
}
Envelope { kem_ct: Vec<u8>, to_receiver: Vec<u8>, to_sender: Vec<u8>, body: Vec<u8> }
Word8 são oito palavras u32 em little-endian, representadas como 64 caracteres hexadecimais minúsculos. A forma de transmissão é bincode(Transaction) com os padrões do bincode (inteiros de largura fixa, prefixos de comprimento de 8 bytes, tags de enum u32), e rand_sendTransaction a recebe em hex. Não há assinatura sobre a transação nem campo de remetente: um pacote se autoriza por suas provas (a prova do pacote e, desde a chain 17, a prova de autorização ao lado dela), e uma ação assinada carrega a assinatura dentro de si. A proteção contra repetição é o conjunto de nullifiers para os pacotes, um nonce por validator no registro para as ações de staking assinadas, um mint_nonce por token para TokenMint e SetAuthority, e o pause_nonce e o list_nonce da bridge para suas ações de governança.
Uma transação carrega no máximo um pacote, e toda ação que não seja sem pacote carrega exatamente um. Os slots 0–1 do pacote carregam um ativo privado e os slots 2–3 carregam RAND, de modo que uma única prova move um token e paga sua taxa em RAND; a queima da bridge com dois pacotes do projeto anterior deixou de existir: uma queima é um único pacote (Notas).
A carteira rand constrói tudo isso por meio de randprotocol_client::wallet::{send, submit}: seleciona as entradas, obtém a âncora, calcula as testemunhas a partir da cópia da árvore que a própria carteira mantém, constrói as saídas e sela os quatro envelopes, obtém o vínculo da transação, produz a prova do pacote e a prova de autorização, confere o digest publicado da prova do pacote e submete.
O vínculo da transação
Uma transação não tem assinatura, e a prova de um pacote se compromete apenas com o digest do próprio pacote: âncora, nullifiers, commitments, taxa, os campos de queima, time. Até a chain 13, nada ligava um pacote provado à ação com a qual viajava nem a seus envelopes; assim, qualquer pessoa que visse uma transação podia manter todos os bytes das provas e alterar o resto: o destino de um BridgeBurn, o validator de um Bond, um envelope. Desde a chain 14, toda prova de pacote é feita sobre o vínculo da transação, e verificada contra ele:
binding(tx) = blake3("rand-tx-bind-1" || bincode(chain_id, bundle', action')) chains 14 to 19
binding(tx) = blake3("rand-tx-bind-2" || bincode(genesis_hash, chain_id, bundle', action')) chain 20
as 8 LE u32 words
onde ' significa que cada sequência de bytes de prova de pacote é substituída pelo vetor vazio: bundle.proof, desde a chain 17 bundle.auth_proof, e o proof de um Aggregate. Todo o resto está dentro: auth_commit, os quatro envelopes, todos os campos de queima, um destino, um validator, um destinatário, as assinaturas, a atestação do guardian e suas co-assinaturas Dilithium2, e a prova própria de um Call. As oito palavras viajam no segmento de entrada pública da prova, cujo digest H_PUB o circuito já vincula; portanto a correção não exigiu mudança no guest nem no circuito; o nó executa Machine::verify_public(hc_bundle, binding, proof) e recusa uma prova de pacote que declare qualquer outra altura pública (public height not the transaction binding's). Transaction::binding é a única função que a carteira chama antes de provar e o ledger antes de verificar. Desde a chain 17, a prova de autorização é feita sobre as mesmas oito palavras e verificada contra elas, de modo que ela também não pode ser movida para outra transação.
A chain 20 vincula o hash da gênese. Até a chain 19, o vínculo, e as mensagens assinadas das ações sem pacote, cobriam apenas o id da chain; assim, duas chains que compartilhassem um mesmo id (um re-corte, uma cópia privada) teriam aceitado as provas e assinaturas uma da outra. A gênese da chain 20 define binding_domain: 1 (v0.6.8, auditoria v6 BIND-1; crates/randprotocol-core/src/types/binding.rs). O hash da gênese passa então a abrir cada preimagem, sob uma tag uma versão acima: rand-tx-bind-2 e rand-call-bind-2 para os dois vínculos, e o mesmo para o mint do faucet (rand-mint-3), Unbond e Withdraw (rand-unbond-2, rand-withdraw-2), as mensagens de mint e de autoridade do RPL (rand-rpl-mint-2, rand-rpl-authority-2), as ações de aggregator e as mensagens de governança da bridge. Um registro de validator numa chain assim é sempre a mensagem v2 (gênese, chain, endereço, pagamento). Nada provado ou assinado para uma chain é verificado em outra, qualquer que seja o seu id de chain. As palavras do vínculo são uma entrada pública, de modo que nenhum guest nem chave do verificador mudou. Uma carteira anterior à v0.6.8 ainda prova contra a forma com o id da chain e é recusada na chain 20. rand_getLimits e rand_status servem binding_domain (1 na chain 20, 0 sem o campo).
Sob a gênese hardening_v6 (chains 16 a 20), a prova própria de um Call também é vinculada, por um segundo digest da mesma construção sob um domínio próprio, com a prova do call zerada além das provas do pacote:
call_binding(tx) = blake3("rand-call-bind-1" || bincode(chain_id, bundle', action'')) as 8 LE u32 words
Na chain 20 a tag é rand-call-bind-2 e o hash da gênese abre a preimagem, como acima. A prova de um Invoke é vinculada da mesma forma, com a transição declarada mantida na preimagem, de modo que a prova se compromete exatamente com a mudança de estado, os pagamentos e os destinatários que a transação carrega.
A prova do call carrega essas palavras em seu segmento de entrada pública, depois da entrada pública do programa definida no deploy, quando ele a tem; assim, uma cópia dela anexada a outro pacote de taxa não é verificada. Os dois vínculos se aninham: a carteira escolhe as notas do pacote de taxa e constrói a transação com as provas vazias, prova o call contra o vínculo do call, insere essa prova e só então obtém o vínculo da transação e prova o pacote.
A consequência para um cliente: construa a transação inteira — saídas, envelopes, ação — antes de provar. Uma carteira construída antes do fork prova contra o segmento público vazio e é recusada. As ações sem pacote não têm prova a vincular e se apoiam em suas próprias assinaturas. O vínculo não é o id da transação, que permanece inalterado.
O id da transação
txid = blake3("rand-txid-3" || bincode(view))
onde view é a transação com os bytes de proof do pacote substituídos por blake3(proof) (proof_hash) e seus bytes de auth_proof por blake3(auth_proof) (auth_proof_hash, depois de auth_commit, que entra como está), com todos os demais campos literais. O domínio era rand-txid-2, sem os dois campos de autorização, até a chain 16; todo id de transação mudou com a chain 17. Calcular o hash da prova pelo seu digest é o que permite que um pacote podado (agregação de blocos, não ativa na chain 20) mantenha o mesmo id de sua forma bruta; um pacote podado mantém sua prova de autorização. Uma transação sem pacote calcula o hash de seus bytes como estão. O mesmo id é a chave do mempool, dos recibos e de toda consulta RPC.
Todas as ações
Action é um enum do bincode, com as tags na ordem de declaração:
| tag | ação | campos | bundle | piso de taxa |
|---|---|---|---|---|
| 0 | None | — | obrigatório | BUNDLE_BASE = 0,001 RAND |
| 1 | Mint | cm, pk, time, r, envelope, amount, minter: PublicKey, signature | deve estar ausente | 0 (assinada pelo validador; apenas em cadeias de faucet, amount ≤ 100 RAND) |
| 2 | Deploy | base_pc: u32, words: Vec<u32>, public: Vec<u32> | obrigatório | base + 100.000 unidades por palavra de programa e por palavra pública |
| 3 | Call | program, proof, input_envelope: Option<CallEnvelope> | obrigatório | na cadeia 20, base + gas_price × o GAS_LIMIT declarado da prova + byte_price por KiB de prova e de envelope de entrada; base + call_fee(tier) em uma cadeia sem seção gas |
| 4 | Bond | validator, amount, registration: Option<Registration> | obrigatório, e burn_r == amount | base |
| 5 | Unbond | validator, amount, nonce, signature | deve estar ausente | 0 |
| 6 | Withdraw | validator, amount, nonce, time, r, envelope, signature | deve estar ausente | 0 na transmissão; a base é deduzida de amount |
| 7 | BridgeAttest | attestation, recipient: ShieldedAddress, r, time, asset, envelope, pq_signatures: Vec<PqSignature> | obrigatório | base |
| 8 | BridgeBurn | asset, amount, relayer_fee, to_chain: u16, token: [u8; 32], to: [u8; 32] | obrigatório, com burn_asset == asset e burn_a == amount | BRIDGE_BURN_FEE = 0,01 RAND |
| 9 | RegisterAggregator | registration | obrigatório, burn_r = a caução | base |
| 10 | UnbondAggregator | aggregator, nonce, signature | deve estar ausente | 0 |
| 11 | WithdrawAggregator | aggregator, nonce, time, r, envelope, signature | deve estar ausente | 0 |
| 12 | SlashAggregator | dois cabeçalhos de agregação assinados | deve estar ausente | 0 |
| 13 | Aggregate | covers, proof, aggregator, nonce, time, r, envelope, signature | deve estar ausente | 0 |
| 14 | RegisterToken | name, symbol, decimals: u8, authority: MintAuthority, initial: Option<InitialMint>, salt: [u8; 32], index: u32 | obrigatório | base + a registration_fee do registro (1 RAND na cadeia 20, queimado) |
| 15 | TokenMint | asset, amount, recipient: ShieldedAddress, r, time, envelope, nonce, signature | obrigatório | base |
| 16 | SetAuthority | asset, new: Option<PublicKey>, nonce, signature | obrigatório | base |
| 17 | TokenBurn | asset, amount | obrigatório, com burn_asset == asset e burn_a == amount | base |
| 18 | PauseMints | nonce, signature | deve estar ausente | 0 |
| 19 | UnpauseMints | nonce, pq_signatures | deve estar ausente | 0 |
| 20 | RegisterBridgedToken | name, symbol, salt, chain: u16, token: [u8; 32], decimals: u8, nonce, pq_signatures | obrigatório | base + registration_fee |
| 21 | ListBacking | token_index, chain: u16, token: [u8; 32], decimals: u8, nonce, pq_signatures | obrigatório | base |
| 22 | RotatePqGuardians | new_pq_guardians: Vec<PublicKey>, nonce, pq_signatures | deve estar ausente | 0 |
| 23 | RotatePauseKey | new_pause_key: PublicKey, nonce, pq_signatures | deve estar ausente | 0 |
| 24 | ClaimVested | entry: [u8; 32], amount, nonce, to: ShieldedAddress, time, r, envelope, signature | deve estar ausente | 0 na transmissão; a base é deduzida de amount |
| 25 | RevokeVesting | entry: [u8; 32], unvested, nonce, to: ShieldedAddress, time, r, envelope, signatures (desde a v0.6.8, um limiar dos revogadores da entrada, pagando ao tesouro que a entrada de gênese indica; antes, uma única signature) | deve estar ausente | 0 na transmissão; a base é deduzida de unvested |
| 26 | BondVested | entry: [u8; 32], validator, amount, registration: Option<Registration>, nonce, signature | deve estar ausente | 0 |
| 27 | UnbondVested | entry: [u8; 32], amount, nonce, signature | deve estar ausente | 0 |
| 28 | AdmitValidator | candidate: PublicKey, signatures: Vec<(PublicKey, Signature)> | deve estar ausente | 0 |
| 29 | SlashEquivocation | first, second: dois cabeçalhos de bloco assinados de um mesmo líder para uma mesma view | deve estar ausente | 0 |
| 30 | RotatePqGuardiansV2 | new_pq_guardians: Vec<PublicKey>, possession: Vec<Vec<u8>>, nonce, pq_signatures | deve estar ausente | 0 |
| 31 | RotatePauseKeyV2 | new_pause_key: PublicKey, possession: Vec<u8>, nonce, pq_signatures | deve estar ausente | 0 |
| 32 | CancelRotation | kind: u8, nonce, signature | deve estar ausente | 0 |
| 33 | Invoke | program, proof, input_envelope: Option<CallEnvelope>, transition: Transition | obrigatório; burn_r, e burn_a de burn_asset, são o que entra no programa | o piso de uma chamada + cell_fee por célula criada |
MintAuthority = None | Key(PublicKey) | Bridge { backings: Vec<Backing> } | Program(Hash)
InitialMint { amount: u64, recipient: ShieldedAddress, r: Word8, time: u32, envelope: Envelope }
PqSignature { index: u8, signature: Vec<u8> } // one guardian's Dilithium2 co-signature, by set index
Na cadeia 20, os tags 0 a 8, 14 a 21, 28 e 30 a 33 estão disponíveis. Seu gênese tem uma seção bridge com rules_v2, uma seção tokens, staking.admission_by_vote (tag 28), bridge.rotation (tags 30 a 32) e program_state (tag 33). Os tags 22 e 23 são recusados ali: sob bridge.rotation.needs_possession, uma rotação deve ser a forma …V2, que carrega a assinatura própria de cada nova chave. Na cadeia 19, que não tinha nenhuma dessas seções, os tags 0 a 8 e 14 a 23 estavam disponíveis (a cadeia 14, sem rules_v2, recusava 22 e 23). SlashEquivocation exige uma seção staking.slashing, e a da cadeia 20 é nula. As cinco ações de agregação exigem uma seção aggregation, que a cadeia 20 não tem (um build se recusa a iniciar com um gênese que a contenha até que a agregação seja medida de novo). Um nó recusa todas elas na verificação de habilitação, antes de qualquer outra checagem, mas elas ainda ocupam seus tags de bincode, de modo que um codificador que fixa os números de tag no código deve contá-las. A agregação está documentada como futura em Provas. As quatro ações de vesting de gênese (tags 24 a 27, presentes no build desde a v0.5.11) exigem uma seção vesting, que nenhuma cadeia teve, a cadeia 20 inclusive, e são recusadas na admissão quando ela falta. Os tags 28 a 33 são novos na v0.6.8 e foram acrescentados ao final, de modo que o histórico de todas as cadeias anteriores continua decodificável.
As quatro ações de RPL (tags 14 a 17) registram um token, emitem mais unidades de um token autorizado por Key, transferem ou renunciam à sua autoridade de emissão, e queimam uma posse; um RegisterToken que nomeie uma autoridade Bridge ou Program é recusado. Não existe ação de transferência de token: a transferência de um token é um None, indistinguível de um pagamento em RAND. Veja Tokens RPL. As quatro ações de governança da bridge (tags 18 a 21) são a pausa de emissão, seu levantamento, e a listagem de um token com bridge ou de um lastro adicional após o gênese. PauseMints é assinada pela chave de pausa do gênese, que só pode pausar; as outras três exigem um quórum dos guardiões Dilithium2 (PQ). Um BridgeAttest também carrega um quórum desses, ao lado do seu quórum ECDSA. Os tags 22 e 23, sob rules_v2 da bridge, substituem o conjunto de guardiões PQ ou a chave de pausa, cada um sob um quórum PQ do conjunto atual e o rotation_nonce da bridge. Os tags 30 e 31 fazem o mesmo sob bridge.rotation e carregam também a assinatura Dilithium2 própria de cada nova chave sobre a mensagem de rotação, uma prova de que a chave é mantida; na cadeia 20, uma rotação é registrada como pendente e passa a valer após delay_secs, 86.400 s. CancelRotation (tag 32), assinada pela chave de pausa atual, descarta uma rotação pendente antes que ela entre em vigor. Veja Bridge.
AdmitValidator (tag 28) é o voto do conjunto de validadores para admitir uma nova chave: assinaturas de validadores que detêm mais de dois terços do stake, em ordem crescente de endereço. Sob staking.admission_by_vote (cadeia 20), um Bond que registraria um novo validador é recusado, a menos que seu endereço tenha sido admitido dessa forma (Staking). Invoke (tag 33, RPL-2) é uma chamada cuja prova atesta uma transition de estado declarada do programa: as células lidas e escritas, o que os campos de queima do bundle trazem para o cofre do programa, e as notas que o cofre paga ou que o programa emite. O ledger aplica a transição se toda leitura declarada ainda for válida e as duas provas forem verificadas (Programas).
Um Deploy e um Call são transações blindadas comuns, cujo bundle é uma autotransferência de zero: ele existe para pagar o piso com as notas do próprio chamador, de modo que a cadeia nunca saiba de quem eram. O RAND sai do pool apenas por burn_r — o stake de um Bond, a caução de um RegisterAggregator, o depósito de um Invoke no cofre de um programa — e um token apenas por burn_a, com burn_asset nomeando-o, em um TokenBurn, um BridgeBurn ou um Invoke. Toda outra ação não queima nada, e um bundle com burn_asset = 0 e burn_a diferente de zero é recusado em toda parte.
Ordem de admissão
Ledger::validate_inner verifica o que é barato antes do que é caro, nesta ordem; a mempool executa a mesma verificação antes de propagar, de modo que uma transação inválida é recusada uma única vez, na borda.
- Limites de tamanho — a transação inteira ≤
max_block_bytesda cadeia; cada um dos quatro envelopes do bundle, e qualquer envelope de note que a ação carregue, exatamente oenvelope_bytesdo genesis (1.860 na chain 20; no máximo 2.048 bytes em uma cadeia sem o campo); a prova do bundle, a prova de autorização e a prova de uma chamada, cada uma ≤max_proof_bytes(2 MiB por padrão, 4 MiB no genesis da chain 20; eram 8 MiB nas chains 13 a 16); um programa ≤ o limite de programa da cadeia (4.096 palavras por padrão, 65.535 no genesis da chain 20); um envelope de chamada ≤max_call_envelope_bytes(18.432 bytes por padrão, 65.536 na chain 20); uma attestation ≤ 16 KiB. Uma prova de bundle na forma de marcador selado (podada) é recusada aqui fora da sincronização em forma selada (PrunedFormOutsideSync), antes de qualquer trabalho de prova. - O chain id confere.
- Formato e piso de taxa — uma ação sem bundle não carrega nenhum bundle, e todas as demais devem carregar exatamente um;
check_burn_shapefixa quais dos três campos de burn a ação pode definir: umTokenBurnouBridgeBurnqueima o seu próprioasseteamountpor meio deburn_asseteburn_a, umBondouRegisterAggregatorqueima o seu valor por meio deburn_r, e toda outra ação não queima nada;fee ≥ fee_floor(action). Em uma cadeia com uma seçãogas(chain 20), o piso de umCallou de umInvokenesta etapa é a regra de gas com um limite declarado de 1,BUNDLE_BASE + gas_price + byte_price × ⌈bytes / 1024⌉sobre a sua prova e o seu envelope de entrada, que são conhecidos antes de qualquer verificação. - Âncora — uma das últimas 1.024 raízes de fim de bloco na chain 20, cujo genesis define
proof_window_blocks: 1024(v0.6.8); uma das últimas 256 em uma cadeia sem o campo, como até a chain 19. - Tempo — dentro de
[height − 1024, height]na chain 20;[height − 256, height]sem o campo. O único valor do genesis define as duas janelas. - Nullifiers e commitments — os quatro nullifiers são distintos e nenhum foi gasto; os quatro commitments são distintos e nenhum já é uma folha.
- Verificações da ação — o faucet está habilitado, o mint está abaixo do limite e assinado por um validator e, na chain 20, nomeia um destinatário da lista de permissão
staking.faucet_recipientsdo genesis, cabe nofaucet_budget_per_epochda época e é assinado por um validator da listastaking.faucet_mintersdo genesis (uma regra de validade desde a chain 16); um mint ou depósito igual ou superior a 2⁶³ é recusado em todas as cadeias; o programa decodifica; o programa existe; as regras de staking (registro presente exatamente quando o validator é desconhecido, o nonce, a assinatura, stake suficiente); as regras próprias da bridge paraBridgeAttesteBridgeBurne para as ações de pausa e de listagem; as regras do RPL para as suas quatro ações, incluindo a taxa de registro; na chain 20, o quórum do voto de admissão e, para umInvoke, o formato da transição, as leituras declaradas contra as células armazenadas, a cobertura do cofre para cada pagamento e a taxa de célula. Tudo isso é executado antes da prova do bundle, de modo que uma transação que não pode ser aplicada não custa nenhuma verificação STARK. Sobhardening_v6(chains 16 a 20), duas outras recusas ficam aqui: umDeployde um programa que nenhuma chamada poderia conter (ProgramUncallable), e uma prova de bundle ou de chamada cujo cabeçalho ou transcrição carrega um campo que o prover honesto não escreveria (non-canonical proof: …), recusada antes de qualquer das provas ser verificada. Desde a v0.6.8, uma prova cujo cabeçalho declara um grau acima do teto da maior tabela é recusada primeiro, sob o mesmo veredito (auditoria v6 ZKV-4), antes de a verificação percorrê-la. - Digest do bundle — em uma cadeia de autorização dividida (toda cadeia desde a 17), os campos de autorização são verificados primeiro: um bundle sem prova de autorização é recusado, o
cque a prova de autorização publica (lido, ainda não verificado) deve ser igual aauth_commite, na chain 20, a prova de autorização deve declarar umGAS_LIMITde exatamente 1.279. Em seguida o ledger recalcula o digest dos campos publicados do bundle,auth_commitentre eles, e ele deve ser igual ao que a prova publicou. Na chain 20, a prova do bundle também deve declarar umGAS_LIMITde exatamente obundle_gas_limitdo genesis, 20.479. - Prova do bundle —
Machine::verify_publiccontra ohc_bundlefixado no genesis e a vinculação da transação. Em uma cadeia de autorização dividida, a prova de autorização é verificada em seguida, contra ohc_authfixado no genesis e a mesma vinculação. - Prova da chamada e a sua taxa por tier — para um
Callou umInvoke, o cabeçalho da prova é fixado primeiro (tier no máximo 14, uma tabela keccak no máximo 2¹², uma tabela sha256 no máximo 2¹³, a altura do programa implantado, com piso de 2⁷ linhas sobhardening_v6e, na chain 20, umGAS_LIMITdeclarado não superior ao tetogas_maxdo cabeçalho), depois o STARK da chamada contra ocode_hashdo programa e, sobhardening_v6, a vinculação de chamada da transação. A taxa é verificada por último, porque só pode ser conhecida depois que a prova é decodificada: na chain 20,fee ≥ BUNDLE_BASE + gas_price × GAS_LIMIT + byte_price × ⌈bytes / 1024⌉nos preços em vigor, maiscell_feepor célula criada para umInvoke(Taxas); em uma cadeia sem uma seçãogas,fee ≥ BUNDLE_BASE + call_fee(tier).
Os erros voltam de rand_sendTransaction como o código -32000 com o motivo: nullifier already spent, anchor is not one of the last 1024 roots (256 em uma cadeia sem proof_window_blocks), time 12 is outside [476, 1500], fee 1000000 below minimum 2000000, the bundle's digest is not what its proof published, invalid bundle proof: …, unknown program …, already in mempool, conflicts with a pending transaction over <nullifier>, faucet is disabled on this chain, e, desde a chain 14, the bundle burns asset 2 on an action that burns no token, a burn of 5 is not allowed on this action, the bundle burns 399, not the 400 the action declares, wrong mint nonce: expected 1, got 0, wrong token index: expected 3, got 2, desde a chain 17, this chain requires an auth proof on every bundle (genesis hc_auth) e the auth proof publishes a different commitment than the bundle's auth_commit, e, na chain 20, envelope is 1348 bytes, this chain requires exactly 1860 e bundle proof declares gas limit …, this chain requires exactly 20479. Aceitação não é confirmação: faça polling em rand_getTransaction até que ele retorne um bloco.
Um bloco que contenha qualquer transação inválida é inválido. A mempool indexa todo nullifier e todo commitment pendente e recusa um conflito de imediato, de modo que dois bundles que gastam a mesma note nunca compartilham um bloco.
Tamanhos
| parte | bytes |
|---|---|
Word8 | 32 |
um Envelope | 1.892 codificados (1.860 de texto cifrado) desde a chain 18; 1.380 (1.348) até a chain 17 |
Bundle menos as suas duas provas | 7.936 (quatro nullifiers, quatro commitments, quatro envelopes, auth_commit) |
| transação de transferência menos as suas duas provas | 7.949 |
| transação de deploy menos as suas duas provas, programa de 100 palavras, sem entrada pública | 8.369 |
| transação de chamada menos as suas três provas, sem envelope de entrada | 7.990 |
| transação de mint (sem bundle) | 5.761 (uma chave Dilithium2 de 1.312 bytes e uma assinatura de 2.420 bytes) |
| uma prova de bundle, tier 14, perfil de produção | 1.497.544 e 1.504.260 lidos na chain 18 (um deploy na altura 97.821 e uma chamada em 98.335, 2026-09-30); 1.422.116 e 1.423.170 medidos na chain 14 (o registro do zUSD e uma listagem de lastro, 2026-09-20) |
| uma prova de autorização, tier 10, perfil de produção | 1.370.827 e 1.367.303 nas mesmas duas transações da chain 18 |
As seis primeiras linhas são a codificação desde a chain 18: o campo de memo acrescentou 512 bytes a cada envelope, e a autorização dividida acrescentou 40 bytes ao bundle antes dos bytes da própria prova de autorização. Assim, uma transferência blindada na rede ocupa cerca de 2,88 MB desde a chain 18, chain 20 incluída, essencialmente tudo prova, qualquer que seja o ativo que ela movimenta: cerca de 1,5 MB de prova de bundle e 1,37 MB de prova de autorização. Eram cerca de 1,43 MB até a chain 15, com uma só prova. Os blocos são limitados a 2.000 transações (MAX_BLOCK_TXS) e ao max_block_bytes da cadeia em bytes de transação, ambas regras de consenso: 4 MiB por padrão (MAX_BLOCK_BYTES), o que comportaria uma dessas transferências, e 20 MiB no genesis da chain 20, que tem espaço para sete. Um genesis com hc_auth deve definir max_block_bytes em pelo menos três vezes max_proof_bytes mais 1 MiB, já que uma transação pode carregar três provas (bundle, autorização, chamada), de modo que o padrão de 4 MiB é recusado ali. A agregação em nível de bloco é o remédio em fila; veja Provas.
Lendo uma de volta
rand_getTransaction(hash) retorna null até que a transação seja confirmada e, depois, a sua posição no bloco e somente os campos públicos:
{
"height": 192, "index": 0, "block_hash": "63f6…08",
"tx": {
"hash": "4f2c…e7", "chain_id": 20,
"bundle": {
"anchor": "6b1d…c4",
"nullifiers": ["8c04…d1", "5e77…20", "03aa…6f", "e19b…42"],
"commitments": ["2a9f…07", "b310…88", "77c1…0e", "5d20…b3"],
"fee": "1000000", "burn_a": "0", "burn_r": "0", "burn_asset": 0, "time": 5,
"proof_len": 1497544, "envelope_len": [1860, 1860, 1860, 1860],
"auth_commit": "0bab…e8a1", "auth_proof_bytes": 1370827
},
"action": { "kind": "none" }
}
}
Não há campos from, to, nonce, valor nem asset numa transferência, e também nada disso no bloco armazenado: o nó não tem mais nada a ocultar. auth_commit e auth_proof_bytes (o tamanho da prova de autorização) existem desde a chain 17. O pacote tem sempre quatro slots, incluindo os dummies, e uma transferência de RAND e uma transferência de qualquer token RPL têm ambas "kind": "none". Todo valor u64 é uma string decimal desde a chain 14 (fee, burn_a, burn_r, o amount de uma ação); índices, nonces, alturas e comprimentos permanecem números JSON. Os demais tipos de ação exibem seus campos públicos — mint, deploy, call, bond, unbond, withdraw, bridge_attest, bridge_burn, register_token, token_mint, set_authority, token_burn, pause_mints, unpause_mints, register_bridged_token, list_backing, rotate_pq_guardians, rotate_pause_key, os quatro tipos de vesting claim_vested, revoke_vesting, bond_vested e unbond_vested, e, desde a v0.6.8, admit_validator, slash_equivocation, rotate_pq_guardians_v2, rotate_pause_key_v2, cancel_rotation e invoke — conforme listados na referência JSON-RPC.