Rand Protocol
Documentação
Programas de exemplo

Quatro programas na chain 20

Quatro programas confidenciais completos implantados na testnet — uma votação anônima, uma prova de passivos, um ledger off-chain e uma decisão de crédito privada — com seus ids de programa, a configuração que compartilham, o que a chain 18 mudou para quem escreve um programa e como chamá-los você mesmo.

Estes quatro programas são, cada um, uma aplicação completa de uma chamada confidencial: um problema, uma interface, um guest em Rust, as ferramentas do lado do host, e um deploy e uma chamada no testnet em produção. São mais longos do que os tutoriais, e cada um se apoia numa parte diferente da máquina. Escrever um programa em Rust ou C é o guia do modelo que todos usam. Leia-o primeiro se read_input, o segmento público ou os oito slots de saída forem novidade para você.

Tudo aqui foi executado pela primeira vez na chain 18 com o fullnode v0.6.7, em 2026-09-30, e toda saída nestas páginas foi colada dessas execuções. Os programas não passam de uma chain cut para a seguinte; por isso, em 2026-10-01 os quatro foram implantados de novo, sem alterações, na chain 19 e depois na chain 20 (fullnode v0.6.8), e as chamadas foram repetidas a cada vez. O id de um programa é um hash do código e do segmento público, não da chain, de modo que todos os ids são iguais; os hashes de transação nas transcrições são os da chain 18, e a tabela abaixo aponta para os da chain 20. Na chain 20 um programa também pode manter estado entre chamadas (RPL-2); estes quatro não o usam, e Estado do programa e o repositório example-programs tratam de programas que o usam.

Os quatro programas

programao que uma chamada provaapoia-se empalavrastier
Uma votação anônimao chamador é membro de uma lista publicada, sem dizer qual, e ainda não votouKeccak-256, um segmento público42712
Uma prova de passivosos saldos privados dos clientes somam no máximo uma reserva publicada, sob uma raiz que cada cliente pode verificarSHA-256, uma árvore construída dentro do programa99414
Um ledger fora da cadeiaum lote de transferências leva um ledger de um estado ao seguinte, com todo remetente autorizado e nada criadoPoseidon2, estado carregado entre chamadas77314
Uma decisão de crédito privadaum modelo publicado aprova ou recusa dados privados de um solicitante, atestados por um attesteraritmética de inteiros, um modelo no segmento público46212

Os quatro estão em produção na chain 20. Cada chamada da tabela repete, com as mesmas entradas, a chamada da chain 18 mostrada na página do respectivo programa:

programaid do programaimplantado na chain 20 emprimeira chamada na chain 20
votação anônimaad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970f6ae5528…0f823a304e7a…427b
prova de passivosfce8474dfdf80b9733e04fb72d4d8d19c40ba5ab646b3d867322daf4b9b599c0a8bc281b…17d96116c15f…d62a
ledger fora da cadeiac4ddd388f2d94299610c7c3409dfdfb4dcb4ae3e0bc9ec78d0c45f51d8231cab72cc95f2…3d519c9998bb…66eb
decisão de crédito privada7bbec14289f4446231b3a9a5ac18cb0613a373ca8a14fe4f046f3fde7ae6c73c413e6658…e42bd3ceb306…124a

O explorador lista cada programa e suas chamadas em randscan.org/programs. Uma chain de testnet é substituída de tempos em tempos, e os programas não passam de uma para a outra: depois do próximo cut esses ids deixarão de existir e os programas precisarão ser implantados de novo.

Configuração

A toolchain. Os guests são compilados com rand-guest, do repositório de circuits, no commit que o fullnode v0.6.7 e o v0.6.8 incorporam ambos:

git clone https://github.com/randprotocol/zkp-circuits circuits
cd circuits
git checkout aeacf31                      # branch feat/v067-circuits
rustup toolchain install 1.98.1
rustup target add riscv32im-unknown-none-elf --toolchain 1.98.1
(cd rand-guest && cargo +1.98.1 build --release)
alias rand-guest="$PWD/rand-guest/target/release/rand-guest"

O commit importa por dois motivos. hc é função do código-fonte e da toolchain, de modo que um guest recompilado em outro commit de guest-sdk pode obter um hc diferente. E as assinaturas do SDK mudaram: em aeacf31, keccak e sha256_compress recebem referências a arrays, onde commits mais antigos recebiam ponteiros brutos.

Os códigos-fonte. Os quatro guests, seus scripts de host e os dados de demonstração formam um único download. Descompacte-os dentro do checkout, porque rand-guest build encontra guest-sdk procurando para cima a partir do diretório do guest:

mkdir -p my-guests
curl -sSf https://randprotocol.org/examples/rand-example-programs.tar.gz | tar -xz --strip-components=1 -C my-guests
ls my-guests
# allowlist  ledger  ledger-host  score  solvency

Os arquivos também podem ser consultados um a um em https://randprotocol.org/examples/programs/, por exemplo allowlist/src/main.rs.

A carteira. Para implantar e chamar é preciso a carteira rand do fullnode v0.6.8 (cargo build --release -p randprotocol-client num checkout do fullnode na tag v0.6.8, ou o rand anexado à release v0.6.8), uma chave e RAND. Uma carteira mais antiga não consegue transacionar na chain 20: a chain 20 vincula cada prova e cada mensagem assinada ao seu genesis, e uma carteira v0.6.7 não o faz.

Um RPC. O endpoint público, https://rpc.randprotocol.org, basta para tudo o que está nestas páginas: ler programas e recibos, sincronizar uma carteira, implantar e chamar. A primeira sincronização de uma carteira lê todos os cabeçalhos de bloco e todos os blocos que contêm uma note, 645 requisições na altura da chain 18 em 2026-09-30, e levou cerca de três minutos pelo endpoint. O endpoint permite a cada endereço 1.200 leituras e 6 envios por minuto. Um nó seu (Executar um nó) não tem esses limites.

export RAND_RPC=https://rpc.randprotocol.org
export RAND_KEY=$HOME/wallet.key.json

Na chain 18, um dos votos contados na página da votação anônima, fdbcc960…fc2b, foi provado num laptop e enviado apenas pelo endpoint público. Nas chains 19 e 20, todas as treze transações em cada vez, os quatro deploys e nove chamadas, seguiram por esse caminho.

O que a chain 18 mudou para um programa

Uma chamada declara um limite de gas e paga por ele. O constraint set 8 mede cada execução. Um ciclo comum custa 1 de gas. As syscalls de hash são as exceções:

instruçãogas
qualquer instrução RV32IM, HALT, WRITE_OUTPUT, READ_INPUT, READ_PUBLIC1
POSEIDON2 sobre n palavras3 + 3 × ⌈n / 4⌉
SHA256, uma compressão64
KECCAK, uma permutação192

A prova carrega um novo valor público, o limite declarado, e o circuito impõe que o gas da execução seja igual ou inferior a ele. O gas exato permanece privado. rand call executa antes o programa no emulador e imprime ambos:

gas: 5636 (declaring 6144, tier 12)

Por padrão, a carteira arredonda o gas exato para cima até um múltiplo de 2^(tier − 2), ou seja, 1.024 no tier 12 e 4.096 no tier 14, de modo que o limite declarado diz pouco sobre a execução. --gas-limit max declara o teto do tier e nada revela além do tier. --gas-limit N declara um valor exato.

A taxa segue o limite declarado e os bytes.

floor = 0.001 RAND  +  gas price × declared limit  +  byte price × ⌈bytes / 1024⌉

Os preços começaram em 100 unidades por gas e 800 unidades por KiB (uma unidade é 10⁻⁹ RAND), e se movem um pouco a cada bloco em direção a uma meta. rand_getLimits serve os preços atuais. A carteira acrescenta duas etapas de preço como margem, um fator de 1,125², porque uma chamada entra dois ou três blocos depois de ser precificada.

Um detalhe decide a maior parte da taxa. Desde a chain 18, a prova de uma chamada é vinculada à sua própria transação, de modo que a carteira precisa fixar a taxa antes de a prova existir, e precifica a prova pelo teto da chain, de 4 MiB, em vez de pelo tamanho real. Para a votação anônima, isso dá (1,000,000 + 100 × 6,144 + 800 × 4,097) × 1.125² = 6.191.437 unidades, 0,0062 RAND, das quais o gas é cerca de um oitavo. As quatro chamadas destas páginas custaram entre 0,0057 e 0,0075 RAND cada. --fee substitui o valor padrão.

Todo bundle são duas provas. A chain 18 executa autorização dividida: o bundle que paga uma taxa carrega uma pequena prova de autorização de tier 10 ao lado da prova do bundle de tier 14. A carteira gera ambas e imprime uma linha para cada. Uma chamada são, portanto, três provas: a da chamada, a da autorização e a do bundle. Num laptop Apple Silicon, as quatro chamadas aqui levaram entre três e cinco minutos e meio cada uma, com pico de 6,1 a 6,6 GB de memória. A prova do bundle responde por cerca de dois minutos disso em todas as vezes. Uma prova de chamada de tier 12 acrescenta meio minuto e uma de tier 14 acrescenta dois minutos.

Os limites em que uma chamada deve caber.

limitechains 18 a 20
tierno máximo 14: 16.383 ciclos, incluídas as linhas de digest
permutações Poseidon22.048 no tier 14, incluídos os digests do programa, da entrada e do segmento público
permutações Keccak128
compressões SHA-256128
prova da chamada4.194.304 bytes (4 MiB)
programa8.180 palavras sem segmento público, algumas a menos com um: a chain 20, como as chains 18 e 19, recusa um deploy que nenhuma chamada de tier 14 conseguiria comportar (ProgramUncallable), seja qual for o max_program_words (65.535)
segmento público32.768 palavras, fixado no deploy
entrada privada com transcrição selada16.071 palavras

rand-guest run --tier 14 verifica os dois primeiros antes do deploy. Uma tabela de hash torna a prova maior. A prova de tier 14 do ledger, sem tabela de hash, tem 1,50 MB. A tabela SHA-256 leva a prova de passivos a 1,91 MB, e a tabela Keccak leva a da votação a 3,35 MB, que é a maior parte do teto de 4 MiB.

Convenções comuns aos quatro

  • O slot 0 é um status, e 0 nunca é um status válido. Todo desfecho, falhas incluídas, tem um número a partir de 1, e uma falha encerra a execução de forma limpa para que possa ser provada. Um recibo com todos os valores zero não pode ser confundido com um resultado.
  • O slot 7 não é tocado. Um guest em Rust que entra em pânico grava 0xdeadbeef (3.735.928.559) ali. Quem lê verifica o slot 7 primeiro.
  • Os slots 2 a 6 carregam um valor de 160 bits: um nullifier, uma raiz, um vínculo, um compromisso. Um hash completo de 256 bits ocupa oito palavras e não deixaria espaço para um status. Truncar para cinco palavras mantém 80 bits de resistência a colisões, e cada página diz o que isso protege.
  • A ordem dos bytes segue o hash. Os valores Keccak são palavras little-endian e os valores SHA-256 são palavras big-endian, porque é assim que cada syscall lê o seu bloco. Um script do host e um guest que divergem nesse ponto produzem digests que simplesmente nunca coincidem.
  • Tudo o que varia por instância está no segmento público; tudo o que varia por chamada é entrada privada. A raiz de uma votação, um valor de reservas e os pesos de um modelo são fixados no deploy e vinculados ao id do programa. Uma chamada não traz palavras públicas próprias.
  • As palavras de entrada são decimais na linha de comando. As páginas transformam um arquivo de testemunha em flags com $(sed 's/[0-9][0-9]*/--input &/g' witness.txt).

Faça uma chamada você mesmo

Com as fontes descompactadas, a toolchain compilada e uma carteira v0.6.8 com fundos, votar como um dos membros da demonstração leva quatro comandos:

cd my-guests/allowlist
python3 allowlist.py witness 10 1 demo-secrets.txt 6 1 > witness.txt    # member 6, choice 1
rand-guest build . --max-words 65535      # only needed to run it in the emulator first
rand call ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970 \
    $(sed 's/[0-9][0-9]*/--input &/g' witness.txt)

Chamar não exige a imagem: rand call busca no nó o código e o segmento público do programa, verifica se o hash deles corresponde ao id e gera a prova sobre eles.

Ler um resultado não exige carteira nem nó. Qualquer recibo é uma única requisição ao endpoint público:

$ curl -s https://rpc.randprotocol.org -H 'content-type: application/json' \
    -d '{"jsonrpc":"2.0","id":1,"method":"rand_getReceipt","params":["8aa13d1f76d28f63a46c30bb09ef44b1ba35083ca80ba7301997f98e5f783666"]}'
{"id":1,"jsonrpc":"2.0","result":{"h_in":"ea71e7701e62d49cac37b239bef132178f481835027d62b34da45fd6cf2667c6","h_pub":"2296cfca8db9d38bd0b7579056c9dea3c8d8005df7b42e2b1e954a75fed67510","height":98335,"index":0,"outputs":[1,2,392974773,873526743,1921224601,3950868247,2248722827,0],"program":"ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970","tier":12,"tx":"8aa13d1f76d28f63a46c30bb09ef44b1ba35083ca80ba7301997f98e5f783666"}}

A API do explorer fornece as chamadas de um programa em uma única requisição: https://randscan.org/api/v1/programs/<program id> retorna call_count e recent_calls (Explorer API).

O que nenhum deles faz

Um programa na Rand é uma prova sobre uma computação, e a chain não age sobre nenhuma de suas saídas. Nenhum desses programas movimenta RAND ou um token, mantém um saldo ou lembra de uma chamada anterior. A votação é contada por quem lê os recibos. Os passivos são comparados com reservas que exigem evidência própria. Os saldos do ledger são números em uma árvore fora da chain. A decisão de crédito é um bit que o credor escolhe honrar. Cada página termina com uma lista do que sua prova deixa a cargo de quem a usa.

Última verificação em relação ao commit 48dc04b do fullnode (chain 18) em 2026-09-30, e circuits `aeacf31` (`guest-sdk/src/lib.rs`, `rand-guest`), the commit fullnode v0.6.7 vendors; `docs/fees.md`, `docs/zkvm.md`, `docs/cli.md` and `crates/randprotocol-client/src/main.rs` at fullnode `48dc04b`; the four deploys and the four calls were made on chain 18 on 2026-09-30, and every receipt was read back from `https://rpc.randprotocol.org`. Onde o código e esta página divergirem, o código está certo; avise-nos.