Rand Protocol
Documentação
Guias

Programas

O modelo de programas confidenciais - um guest RISC-V provado na zkVM, implantado uma única vez e chamado com entradas privadas - com os comandos de build, deploy, call, invoke e receipt, o estado de programa RPL-2 na chain 20 e uma lista honesta do que ainda não é possível.

Use esta página para colocar um programa na cadeia e chamá-lo: o que é um programa, como construir um, o que uma chamada publica e o que oculta, como ler um recibo de volta e os limites que você vai encontrar. Todo programa é código público; toda chamada é uma prova. Programas de exemplo traz aplicações completas, e randprotocol/example-programs traz o código-fonte dos programas implantados na chain 20, incluindo os RPL-2 (abaixo).

O que é um programa

Um programa é código de máquina RV32IM para a zkVM da Rand: { base_pc: u32, words: Vec<u32> }, com no máximo o limite de programa da cadeia (65.535 palavras na chain 20, como em toda cadeia desde a chain 13; 4.096 em um genesis sem max_program_words, como a chain 12), com base_pc múltiplo de 4 e cada palavra decodificando como uma instrução (uma palavra que não decodifica é rejeitada na implantação, nunca em tempo de execução). A chain 20, assim como a chain 19, também se recusa a implantar um programa que nenhuma chamada conseguiria comportar: mais de 8.180 palavras sem segmento público, menos com um (abaixo). Ele é endereçado por conteúdo e imutável:

program_id = blake3("rand-program" || base_pc || words)       // the id, with no public input
program_id = blake3("rand-program-2", base_pc || u32_le(len(words)) || words || u32_le(len(public)) || public)   // deployed with --public
code_hash  = hc = Poseidon2 digest of (base_pc, len, words)    // what calls are verified against

Uma chamada é uma prova STARK de que o programa com digest hc foi executado sobre algumas entradas privadas, gastou no máximo um limite de gas declarado e publicou oito palavras de saída. O nó verifica a prova, registra um recibo e nada mais acontece: saídas são dados, não efeitos. Uma chamada não pode mover RAND; quem chama move valor no bundle que paga pela chamada, e as saídas de um programa significam o que o programa e quem o chama combinarem que significam. Na chain 20, um Invoke é a exceção: sua prova atesta uma transição de estado declarada, que o ledger então aplica (Estado de programa (RPL-2)).

O que o guest pode fazer:

grupoinstruções
superiores / saltosLUI AUIPC JAL JALR
desviosBEQ BNE BLT BGE BLTU BGEU
cargas / armazenamentosLW SW LB LH LBU LHU SB SH
ALUADDI SLTI SLTIU XORI ORI ANDI SLLI SRLI SRAI ADD SUB SLL SLT SLTU XOR SRL SRA OR AND
RV32MMUL MULH MULHU MULHSU DIV DIVU REM REMU
sistemaECALL; nunca FENCE, CSRs, EBREAK, nem as extensões A ou C

Chamadas de sistema, por número em a7:

#chamada de sistemaefeito
0HALTencerra a execução
1WRITE_OUTPUT slot wordfixa a saída pública slot < 8; cada slot no máximo uma vez, slots não escritos valem zero
2READ_INPUT idxretorna a palavra de entrada privada idx, vinculada ao compromisso com sal H_IN
3POSEIDON2 ptr ncalcula o hash de n ≤ 4096 palavras no próprio lugar com a esponja Poseidon2
4KECCAK ptruma permutação Keccak-f[1600] no próprio lugar; faz a prova carregar a tabela keccak
5SHA256 ptruma compressão SHA-256 no próprio lugar; faz a prova carregar a tabela sha256
6READ_PUBLIC idxretorna a palavra idx do segmento público; na chain 20 o segmento público é fixado na implantação (rand program deploy --public); em um genesis sem max_program_public_words ele é vazio e esta chamada não lê nada
7POSEIDON2_LEN ptr nPOSEIDON2 com o comprimento da mensagem vinculado à esponja, de modo que [a] e [a, 0] tenham hashes diferentes (conjunto de restrições 7, chain 16 e posteriores). rand-guest check nos circuits aeacf31 ainda a recusa (próxima seção)

Memória: 32 registradores, com x0 igual a zero; RAM que começa zerada; todo endereço abaixo de 2³⁰; uma pilha de 64 KiB para guests compilados. Um programa construído com rand-guest carrega seus dados somente leitura e inicializados no contêiner de imagem, e um prólogo de carregamento que faz parte do programa grava esses dados antes de o código do próprio programa ser executado (A ISA RISC-V). Compromissos de nota, nullifiers e verificações de Merkle não são chamadas de sistema, mas rotinas de biblioteca sobre POSEIDON2.

Desde a chain 18 (conjunto de restrições 8), toda execução é medida em gas. Uma instrução comum e as chamadas de sistema 0, 1, 2 e 6 custam 1 gas cada, POSEIDON2 sobre n palavras custa 3 + 3 × ⌈n / 4⌉, uma permutação KECCAK custa 192 e uma compressão SHA256 custa 64. Programas de exemplo traz a tabela e o que uma chamada declara.

Construindo um

Três caminhos, todos produzindo palavras RV32IM:

O assembler embutido. A carteira traz cinco guests montados à mão e os monta em um arquivo JSON { "base_pc", "words" }:

rand program build --guest private_payment --arg 1000 --out pp.json   # prints the program id
rand program build --guest balance_check --arg 1000 --out bc.json
# guests: fib, memcpy, bubble_sort, balance_check, private_payment, public_echo, rpl2_counter (v0.6.8); --arg is repeatable

Programas compilados, com rand-guest. O rand-guest do repositório de circuits compila um crate Rust no_std (riscv32im-unknown-none-elf, Rust 1.98.1) ou um diretório C (clang 23.1.1) em um contêiner de imagem, confere cada palavra contra o decodificador da máquina e imprime hc e o id do programa. A compilação é hermética, de modo que o mesmo código-fonte gera o mesmo hc em qualquer máquina. Para as chains 18 a 20, compile a toolchain no commit aeacf31 dos circuits (branch feat/v067-circuits), o commit que o fullnode v0.6.7 e o v0.6.8 fixam. Nesse commit, rand-guest check lista apenas as chamadas de sistema 0 a 6; assim, um guest que chama guest_sdk::poseidon2_len (chamada de sistema 7) é rejeitado na compilação, embora a máquina a execute (zkp-circuits issue #1). Passo a passo: Implantar um programa Rust e Implantar um programa RISC-V.

Programas traduzidos. sbpf2rv traduz um ELF sBPF da Solana, e evm2rv traduz bytecode de runtime EVM, em programas cujas saídas são iguais, palavra por palavra, às dos guests interpretadores da Rand: O transpilador da Solana e O transpilador da Ethereum.

A própria carteira não tem compilador. rand program deploy aceita um .json, um .bin bruto de palavras little-endian ou o contêiner de imagem que o rand-guest grava (fullnode v0.3 e posteriores).

Implantando

rand program deploy pp.json          # a bundle pays the floor; waits for the commit; prints the id
rand program show <id>               # id, base_pc, words_len, code_hash, deployed_at, public_words_len, public_digest

A testnet é a chain 20, e implantar, chamar ou invocar nela exige a carteira rand do fullnode v0.6.8 ou posterior. Uma carteira mais antiga não consegue enviar lá de forma alguma: o genesis da chain 20 define binding_domain: 1, de modo que cada vinculação de prova e cada mensagem assinada inclui o hash do genesis, e as vinculações de uma carteira v0.6.7 carregam apenas o id da cadeia. O endpoint público https://rpc.randprotocol.org atende a sincronização, as implantações e as chamadas de uma carteira (configuração).

A carteira primeiro imprime o id do programa, o número de palavras e hc, e pede ao nó a taxa com rand_estimateFee, que recusa um programa acima do limite da cadeia antes de qualquer prova. Em seguida, monta um bundle de autotransferência de valor zero (o único bundle de ativos ocultos de 4 entradas e 4 saídas que toda transação carrega; a taxa sai de seus slots de RAND) que paga 0,001 RAND + 100.000 unidades × palavras com as próprias notas (as palavras do segmento público contam como palavras de código), anexa Action::Deploy { base_pc, words, public }, prova o bundle contra a vinculação da transação e o envia. Desde a chain 17, são duas provas, e a carteira imprime uma linha para cada: uma autorização de gasto de tier 10 (segundos, cerca de 1,37 MB) e o bundle de tier 14 (cerca de dois minutos em um laptop, cerca de 1,5 MB). Antes da chain 17, era apenas a prova do bundle, cerca de 100 s e cerca de 1,42 MB na chain 15. A taxa de implantação não tem termo de gas nem de bytes: o gas precifica apenas chamadas. O nó confere que cada palavra decodifica, calcula hc, armazena o registro e começa a construir a chave do verificador em segundo plano. O código é público; quem pagou por ele não é: rand_getProgram não tem campo deployer. Implantar as mesmas palavras duas vezes é idempotente.

→ {"method":"rand_getProgram","params":["675adeea…0065"]}
← { "id": "675adeea…0065", "base_pc": 0, "words_len": 42, "code_hash": "…", "deployed_at": 10,
    "public_words_len": 0, "public_digest": null }
→ {"method":"rand_getProgramCode","params":["675adeea…0065"]}
← { "base_pc": 0, "words": [ … ] }

Chamando

rand call <program id> --input 400 --input 250 --input 300 --input 75
rand receipt <tx hash>

A carteira obtém do nó o código e o segmento público e confere que o hash deles corresponde ao id. Ela executa a chamada no emulador e imprime o gas e a taxa antes de provar qualquer coisa:

gas: 5636 (declaring 6144, tier 12)
fee 0.006191437 RAND (incl. two price steps of headroom)

Em seguida, sela a transcrição de entrada (abaixo), prova a chamada localmente sob o perfil FRI da cadeia, com as entradas como o vetor privado, prova o bundle que paga a taxa, envia, aguarda e imprime o recibo. Na chain 20, a prova da chamada é vinculada à sua própria transação (uma vinculação que inclui o hash do genesis) e o bundle são duas provas, de modo que uma chamada são três: a chamada, a autorização de gasto de tier 10 e o bundle de tier 14. As duas linhas acima são da chamada da votação anônima na chain 18, em 2026-09-30 (Uma votação anônima).

A tarifa. Na chain 20, o piso é BUNDLE_BASE + gas_price × declared gas limit + byte_price × ⌈bytes / 1024⌉, em que BUNDLE_BASE é 0,001 RAND, os bytes são a prova da chamada mais o envelope de entrada, e os preços começaram em 100 e 800 unidades (uma unidade é 10⁻⁹ RAND) e se movem com o preenchimento dos blocos. A chain 20 os limita a 10.000 e 80.000 unidades, e apenas os bytes que pagam o preço por byte (provas e envelopes das chamadas) contam para a sua carga (byte_load: "paying"); nas chains 18 e 19 os preços não tinham teto e os bytes de toda transação moviam o preço por byte. O cronograma fixo do ledger, 0.001 RAND + call_fee(tier), a regra da chain 15, não se aplica ali. A carteira fixa a tarifa antes de a prova existir; por isso precifica a prova no limite de 4 MiB da chain e soma uma margem de dois passos de preço. Quatro chamadas medidas na chain 18 custaram de 0,0057 a 0,0075 RAND cada (a aritmética). rand fee call <tier> --gas <limit> --bytes <B> pergunta ao nó o próprio piso. private_payment lê quatro saldos privados e, se eles somarem pelo menos o seu limiar, publica o excedente: saídas [1, 0, 25, 0, 0, 0, 0, 0]. A chain vê a prova, o tier e essas oito palavras; nunca vê os saldos.

Flags: --tier T força um tier; --gas-limit <N|max> declara um limite de gas exato ou o teto do tier no lugar do padrão, o gas exato arredondado para cima a um múltiplo de 2^(tier − 2); --fee <RAND> define a tarifa; --expect-public <FILE> recusa antes de provar, a menos que o segmento público do programa seja as palavras desse arquivo; --auditor <rand1…> sela também a transcrição para um auditor nomeado; --print-call-key imprime a chave por chamada; --no-envelope não publica transcrição; --cuda seleciona o backend de GPU (não testado em hardware e incompatível com selar um envelope). A chain 20 recusa --cuda em uma chamada de forma categórica: uma prova de chamada vinculada à sua transação é feita no backend de CPU.

Recibos

Um recibo é armazenado por chamada confirmada e recalculado quando um nó sincroniza ou verifica a sua chain:

→ {"method":"rand_getReceipt","params":["b082…09b0"]}
← { "tx": "b082…09b0", "program": "675adeea…0065", "tier": 14,
    "outputs": [1, 0, 25, 0, 0, 0, 0, 0], "height": 105, "index": 0, "h_in": "9c0e…7f", "h_pub": null }

h_in é o compromisso com salt da prova para as entradas privadas; por si só, não revela nada. h_pub é o digest do segmento público do programa definido na implantação, o public_digest do programa, ou null para um programa implantado sem um. Não há campo effect, e o limite de gas declarado não consta do recibo. O explorer mostra o mesmo objeto como receipt em GET /transactions/:hash para uma call, lista as chamadas de um programa em GET /programs/:id e filtra GET /transactions?kind=call&program=<id>.

Envelopes de entrada: revelar uma chamada depois

Por padrão, rand call publica a transcrição (salt, inputs) da chamada selada em um envelope de entrada da chamada, de modo que as entradas possam ser mostradas depois sem uma segunda prova:

partebyteso que é
body12 + 16 + 4·n + 16salt ‖ inputs sob uma chave nova por chamada K_call
to_sender60K_call envolvida na chave de visualização de saída do chamador
kem_ct1.088 ou 0uma encapsulação ML-KEM-768 para o auditor, vazia sem auditor
to_auditor60 ou 0K_call envolvida sob o segredo dessa encapsulação

Os dados associados do corpo são H_IN, de modo que um envelope transplantado para outra chamada não abre para ninguém. A chain verifica apenas o tamanho (no máximo max_call_envelope_bytes: 65.536 bytes na chain 20, 18.432 em um genesis sem o campo) e o serve sem alterações com rand_getCallEnvelope(tx). Três chaves o abrem, e nenhuma além delas: a chave de visualização do chamador (toda chamada feita por aquela carteira), a chave por chamada (aquela chamada) e a chave de visualização do auditor (aquela chamada).

rand open-call <txhash>                    # as the caller
rand open-call <txhash> --as-auditor       # as the auditor
rand open-call <txhash> --call-key <hex>   # with the per-call key alone

open-call abre a transcrição, verifica input_digest(salt, inputs) == H_IN, executa novamente o programa sobre as entradas recuperadas por meio do emulador, compara as saídas com as do recibo — e encerra com código diferente de zero se qualquer verificação falhar, para que um script não confunda um aviso com um sim. --no-envelope é irreversível: uma vez que o salt se perde, ninguém, nem mesmo o chamador, pode abrir aquela chamada.

Desde a v0.6.8, a nova execução é feita sobre o segmento público sobre o qual a prova foi feita: as palavras públicas do programa definidas na implantação e, sob hardening_v6, a vinculação de chamada de oito palavras depois delas (issue #116 do fullnode). Na v0.6.7, open-call funcionava apenas para um programa implantado sem segmento público; para um programa com segmento, ele imprimia as entradas e depois parava com re-running the program on these inputs failed: PublicIndex(0), encerrando com código diferente de zero sem executar nenhuma das duas verificações.

Estado do programa (RPL-2)

A chain 20 é a primeira chain com RPL-2, ativado pela seção program_state do seu genesis. Um programa ganha três coisas que não tinha (docs/program-state.md do fullnode, crates/randprotocol-core/src/ledger/program_state.rs na v0.6.8):

cellsum mapa de uma chave de 8 palavras para um valor de 8 palavras, por programa. Uma célula ausente é lida como oito zeros; escrever oito zeros a remove. Comprometida na raiz de estado
um vaultum saldo público por ativo (0 é RAND). O valor entra pelo bundle de uma invoke e sai como notes que a chain calcula
um token próprioum token registrado com rand token create --program <id> (autoridade de emissão Program(id), sem suprimento inicial) é emitido e queimado apenas pelas invokes desse programa

Células e saldos do vault são públicos, como o suprimento de um token. Nada neles é um note.

Invoke (Action 33) é uma chamada que também declara uma transição, e a sua prova de chamada mostra que o programa aceita exatamente essa transição:

Invoke     { program, proof, input_envelope, transition }
Transition { reads:  [{ key, value }]     // cells read, with the values read; keys strictly ascending
             writes: [{ key, value }]     // cells written; keys strictly ascending
             inflow: none | deposit | burn
             pays:   [{ asset, amount, recipient, r, envelope }]   // notes out of the vault
             mints:  [{ asset, amount, recipient, r, envelope }] } // new units of the program's own token

O que entra é o que o bundle queima: burn_r é RAND depositado no vault; burn_a de burn_asset é um token depositado (inflow deposit) ou destruído (inflow burn, apenas o token do próprio programa). O que sai é um note calculado pela chain por pagamento, primeiro os pagamentos e depois as emissões, com o valor e o pk do destinatário públicos, como os de uma emissão. O ledger aplica a transição apenas se o programa existir, a forma for válida, todas as células em reads ainda contiverem o valor declarado, o vault (após o depósito desta própria transação) cobrir todos os pagamentos, toda emissão for do token do próprio programa e as provas forem verificadas. Uma leitura que deixou de corresponder é recusada como StaleRead; a carteira lê de novo e prova de novo.

Como uma invoke difere de uma call.

Call (RPL-1)Invoke (RPL-2)
segmento público sobre o qual a prova é feitapublic ‖ call_bindingpublic ‖ call_binding ‖ context, a transição como palavras
o que a chain faz em caso de sucessoarmazena um recibo; as saídas são dadosarmazena o recibo e aplica a transição: escreve células, move o vault, acrescenta notes de pagamento
valorapenas a transferência do próprio bundledepósitos no vault por meio das queimas do bundle; pagamentos e emissões a partir dele
depende do estadonãoa prova não (o segmento é construído a partir da transação), mas a verificação de leitura sim, na admissão e no pool
tarifao piso da callo piso da call mais cell_fee por célula criada

Um programa lê a palavra de contexto i com READ_PUBLIC(public_len + 8 + i). A palavra 0 é a versão (1), as palavras 1 a 4 são as contagens de leituras, escritas, pagamentos e emissões, depois burn_r, o tipo de inflow, burn_asset e burn_a, depois cada leitura e escrita como chave e valor (8 palavras cada), depois cada pagamento como ativo e valor. Os destinatários não estão no contexto: o programa decide os valores, a vinculação fixa quem é pago. Um programa deve restringir toda palavra de contexto que lhe é mostrada e não pode ter nenhum caminho que cause panic, porque o panic handler do guest-sdk interrompe a execução, e uma execução interrompida é uma execução provável.

Limites da chain 20 (rand_getLimits.program_state):

limitechain 20
cell_fee10.000.000 unidades, 0,01 RAND, por célula criada (uma célula escrita com valor diferente de zero onde nada estava armazenado); reescrever ou apagar uma célula é gratuito
max_reads8 células
max_writes8 células
max_payouts4 notes, pagamentos e emissões juntos

A regra do segmento costuma ser mais restritiva: o segmento inteiro deve caber na tabela pública sobre a qual uma chamada ao mesmo programa é provada, 127 palavras para um programa sem entrada pública. Isso deixa 119 palavras de contexto, por exemplo três leituras, três escritas e quatro pagamentos.

Comandos e RPC (carteira v0.6.8):

rand program build --guest rpl2_counter --out counter.json   # one cell, incremented by one per invoke
rand program invoke <id> --transition step.json [--input N | --inputs-file F.json]
rand program state <id> [--cell <64 hex>]                   # rand_getProgramCell / rand_getProgramCells
rand program vault <id>                                      # rand_getProgramVault

program invoke verifica cada entrada de reads contra a célula atual antes de provar e encerra com código 3 diante de uma leitura obsoleta, antes de provar ou na inclusão, de modo que um script lê de novo e tenta outra vez. rand_estimateFee com "kind": "invoke" cota a tarifa. O formato do arquivo de transição está em docs/cli.md do fullnode. Programas RPL-2 completos implantados na chain 20 (amm, stableswap, orderbook, stablecoin, lending, perp, crowdfund, stoploss) e programas RPL-1 (sealed-auction, ballot, eligibility, private-join, credit-score) estão em randprotocol/example-programs.

O que é público e o que fica oculto

públicooculto
o id do programa e o seu código; o tier; o limite de gas declarado; as oito saídas; H_IN; a taxa e os campos públicos do bundle; o tamanho do envelope; se a chamada usou Keccak ou SHA-256 e, aproximadamente, com que frequência (o cabeçalho da prova declara a altura de cada tabela de hash)as entradas privadas; registradores, memória, ramificações; a contagem real de ciclos e o gas exato; o salt de H_IN; quem fez a chamada e com o que pagou

Duas provas da mesma execução são sequências de bytes diferentes. Isso não bastava na chain 15 e anteriores: uma chamada com 31 ou menos palavras privadas provava uma tabela pequena demais para ocultar, e suas entradas podiam ser recuperadas a partir da prova. O prover fixa um piso de 128 linhas para essas tabelas desde a v0.6, e o constraint set 7 (chain 16 e posteriores) mascara os totais por tabela que ainda vazavam. hc é vinculante, mas não ocultante: quem puder enumerar programas candidatos consegue confirmar qual deles foi implantado.

O que ainda não é possível

Nos termos em que o fullnode os declara:

  • Sem chamadas entre programas e sem estado fora do RPL-2. Um Call é uma prova sobre uma execução; nada passa de uma chamada para outra, exceto o que o chamador devolve como entrada. Na chain 20, um Invoke lê e escreve as células e o vault do próprio programa (acima); nenhuma chain anterior à 20 tem estado de programa, e um invoke não pode tocar as células de outro programa.
  • Sem transferências conduzidas por programa fora de um vault. O effect kind 1, a transferência paga pelo programa para uma conta, foi removido junto com as contas, e as saídas de um Call são declarações. Na chain 20, um Invoke paga a partir do vault do próprio programa, no máximo quatro notes, com valores e destinatários públicos.
  • Sem compilador na carteira. Os programas são os guests montados que já vêm embutidos, ou imagens construídas com rand-guest no repositório de circuitos.
  • A entrada pública é fixada no deploy, não a cada chamada. O constraint set 6 acrescentou READ_PUBLIC e H_PUB. Na chain 20, um deploy pode carregar até 32.768 palavras públicas, o id do programa as vincula, e a prova de cada chamada é verificada contra o digest delas. Não há palavras públicas por chamada.
  • As syscalls de hash geram provas grandes. Uma prova com Keccak tem cerca de 3,2 MB, acima do limite padrão de prova de 2 MiB; uma prova com SHA-256 acrescenta cerca de 400 KB. O limite da chain 20 é 4.194.304 bytes (4 MiB, como nas chains 17 a 19; era 8 MiB nas chains 13 a 16), o que admite ambas: na chain 18, uma chamada de tier 12 com a tabela Keccak mediu 3,35 MB, e uma chamada de tier 14 com a tabela SHA-256 mediu 1,91 MB (Example programs). Uma chamada também é limitada a 128 permutações Keccak-f e 128 compressões SHA-256 (abaixo), e cada KiB de prova é cobrado ao preço por byte.
  • Programas EVM e Solana não são implantados na chain 20, e suas chamadas excedem o limite de tier de chamada. Os guests interpretadores (evm.bin, 18.009 palavras; sbpf.bin, 8.317) e as imagens traduzidas (o ERC-20, 11.686 palavras; SPL Token, 65.096) estão acima do limite padrão de 4.096 palavras e abaixo das 65.535 que a chain 13 definiu e todas as chains seguintes mantiveram. As duas imagens traduzidas foram implantadas na chain 13 em 2026-09-19, o SPL Token com o seu ELF como entrada pública. As chains 14 e 15 foram criadas do zero, e nenhuma das imagens foi reimplantada em nenhuma delas. A chain 20, como a chain 19, recusa implantar qualquer um dos quatro: pela sua regra hardening_v6, um programa que nenhuma chamada de tier 14 conseguiria comportar, com mais de 8.180 palavras sem segmento público e menos com um, é recusado no deploy (ProgramUncallable), para que não se pague uma taxa de deploy por um programa que ninguém pode chamar. Uma chamada ERC-20 é provada no tier 16 ou 18: o approve de tier 16 foi chamado na chain 13 em 2026-09-19 com uma prova de produção de 3.412.405 bytes (Ethereum transpiler), e a chain 20 recusa qualquer chamada acima do tier 14 (próximo item), de modo que hoje nenhuma chamada ERC-20 é admissível ali. As chamadas SPL Token são de tier 20, que exige cerca de 330 GB; não existe nenhuma prova de chamada SPL (proving memory). Os opcodes EVM de contexto de bloco geram trap, e não há estado EVM on-chain.
  • Uma chamada é provada no tier 14 ou abaixo. Desde o fullnode v0.5.6 (2026-09-25), uma prova de chamada acima do tier 14 é recusada (CallTierTooHigh), assim como uma com mais de 128 permutações Keccak-f ou 128 compressões SHA-256. O approve ERC-20 de tier 16 da chain 13 seria recusado na chain 20, como teria sido nas chains 15, 18 e 19, e o mesmo vale para toda chamada ERC-20 de tier 18 ou SPL Token de tier 20; essas ainda são provadas off-chain (the budget).
  • A prova por GPU nunca rodou em hardware. --cuda exige um build com --features cuda e PTX commitado; nenhum dos dois existe na frota. Na chain 20, a prova de uma chamada é feita no backend de CPU, qualquer que seja o build.
  • hc não é ocultante, como acima, e uma solicitação de witness ainda informa ao nó qual leaf uma carteira gasta.

Última verificação em relação ao commit c0878c3f do fullnode (chain 21) em 2026-10-12. Onde o código e esta página divergirem, o código está certo; avise-nos.