Rust para Programas Solana
Código Solana on-chain é Rust, mas não é Rust de laptop com um main diferente. Você compila para sBPF, roda dentro do SVM e só toca estado da chain através de contas e syscalls.
Esse subconjunto - no_std, alocação restrita, entrypoint fixo, layouts Borsh (ou zero-copy), matemática checked, erros tipados e orçamentos de compute/tamanho - é a linguagem de todo programa nativo, Anchor e leve no Agave 4.1.1.
Resumo
- Programas Solana são executáveis sBPF escritos em um subconjunto Rust amigável a
no_std: um entrypoint, dados de instrução e conta em nível de byte (geralmente Borsh), aritmética segura, erros fail-fast e limites rígidos de compute units e tamanho de binário. - Por que Importa: Hábitos de desktop (
unwrap, crescimento ilimitado deVec, depsstd, overflow silencioso, logging pesado) ou falham ao compilar paracargo build-sbfou passam nos testes e quebram sob CU, rent e pressão de auditoria na mainnet-beta. - Conceitos Chave: sBPF / SVM, no_std / alloc, entrypoint, AccountInfo, Borsh, aritmética checked, ProgramError, compute units (CU), tamanho do programa.
- Quando Usar Este Modelo: Escrever ou revisar qualquer crate on-chain (nativo, Anchor 0.32.1 ou Pinocchio); onboarding de engenheiros Rust que não enviaram código BPF/SBF; decidir o que aprender a seguir nesta seção.
- Limitações / Trade-offs: Você troca ergonomia de host por determinismo, agendamento paralelo e execução mensurável. Features como runtimes async, threads e padrões de heap sem limite não se aplicam.
- Tópicos Relacionados: Rust for Solana Basics, no_std & the Program Environment, Borsh Serialization, Safe Arithmetic, Error Handling On-Chain, Program Size & CU Discipline.
Fundamentos
O source de programa Solana é Rust 1.91.1 (pin deste site), mas o target não é um triple de host.
cargo build-sbf (via ferramentas de plataforma Agave / build Anchor) cross-compila para sBPF (Solana Bytecode Format). Validadores carregam o .so e o SVM executa sob locks Sealevel e metering de compute.
Não há main de processo. O loader invoca um único entrypoint que o crate registra (nativo entrypoint!, ou dispatcher gerado pelo Anchor). Essa função recebe program_id, um slice de AccountInfo e bytes de instruction_data. Variantes de instrução, layouts de conta, PDAs e CPIs constroem sobre esse trio.
O ambiente é no_std: sem filesystem, sockets, threads ou biblioteca padrão completa. alloc opcional dá Vec / String / Box, mas uso de heap custa compute. Código de produção prefere contas de tamanho fixo, parsing amigável à stack e evitar format! em caminhos quentes.
Estado durável não vive no binário do programa. Registros do app ficam em contas que o programa possui (ou que outros programas possuem, mutados só via CPI). Structs Rust on-chain são schemas sobre bytes de conta.
Borsh é a codificação de fato para instruções e a maioria dos structs de conta (padrão Anchor 0.32.1). Layouts fixos grandes costumam usar zero-copy bytemuck. Dinheiro e contadores usam u64 (ou inteiros fixos mais largos) com ops checked. Falhas retornam ProgramError (ou códigos custom mapeados); prefira ? a unwrap. Execução é medida em compute units, e tamanho de binário afeta custo de deploy. "Compila" não é "cabe nos orçamentos de CU e tamanho."
Mecânica e Interações
De source Rust a sBPF
Crates nativos dependem de solana-program (ou Pinocchio). Projetos Anchor usam anchor-lang = "0.32.1" para geração de entrypoint e validação de contas.
Fluxo de build: compilar para ELF sBPF com ferramentas de plataforma, fazer deploy com Solana CLI 3.0.10 / Anchor, armazenar bytecode em ProgramData sob o loader upgradeable; em cada instrução o SVM mapeia contas, chama o entrypoint, mede CU e commita ou faz rollback.
Testes unitários de host (#[cfg(test)]) rodam na workstation; testes de integração usam LiteSVM 0.6.x, Surfpool 0.12.0 ou validador local.
Entrypoint e dispatch
Estilo nativo:
use solana_program::{
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
pubkey::Pubkey,
};
entrypoint!(process_instruction);
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
// decodifica instruction_data, valida contas, muta estado owned
Ok(())
}Dentro do handler você geralmente:
- Decodifica um enum de instrução Borsh (ou tag raw + payload)
- Percorre contas com
next_account_infoou checagens de índice - Verifica owners, signers, PDAs e comprimentos de data
- Aplica atualizações de estado checked
- Retorna
Ok(())ouErr(...)
Anchor envolve isso em módulos #[program] e discriminators de 8 bytes. O modelo de runtime não muda: um entrypoint, contas declaradas, dados em byte.
no_std e o que você pode importar
On-chain você tem core, crates no_std selecionados, syscalls solana_program (log, CPI, sysvars), alloc opcional e serializadores como Borsh / bytemuck que compilam para sBPF.
Você não tem std completo, runtimes async, clientes HTTP ou acesso a filesystem. Se cargo build-sbf falha numa dependência, audite cargo tree por std transitivo e troque por crates seguros para programa.
Borsh como formato wire
Dados de instrução e corpos de conta são bytes. Borsh é determinístico e little-endian: inteiros fixos, bool de 1 byte, Pubkey de 32 bytes, Vec/String com prefixo u32, e discriminants de enum.
Dimensione contas na criação para que serialize não escreva além de account.data.len(). Mudanças de schema precisam de versionamento ou migrações; reordenar variantes de enum muda tags silenciosamente. Zero-copy (Pod + Zeroable + #[repr(C)]) reinterpreta contas fixas grandes sem cópia completa quando CU importa.
Aritmética segura
Quantidades de token, taxas em basis points, matemática de shares e índices de reward devem usar:
let next = balance
.checked_add(amount)
.ok_or(ProgramError::InvalidArgument)?;Prefira checked_* quando overflow é erro, saturating_* quando um cap é intencional, e checagens explícitas de limites para regras de negócio (max supply, max bps). Não confie em panics de overflow só em debug; trate wraparound como falha de design.
Erros que clientes podem decodificar
ProgramResult é Result<(), ProgramError>. Mapeie falhas de domínio para códigos custom (impls From nativos, #[error_code] Anchor ou similar) para que clientes TypeScript com @solana/kit 7.0.0 possam ramificar em números de erro em vez de só raspar logs.
Use msg! com moderação para diagnóstico. Logs custam CU e não substituem erros estruturados em caminhos de falha esperados (não autorizado, slippage, expirado).
CU e tamanho do programa
CU vem de um orçamento no nível da transação (na Solana moderna incluindo Agave 4.1.1, planeje em torno de um default perto de 1.400.000 CU a menos que instruções Compute Budget mudem o limite dentro dos caps do protocolo).
Sumidouros comuns de CU:
- Deserializar contas enormes a cada instrução
- Loops sem limite sobre listas fornecidas pelo cliente
msg!verboso em loops apertados- Pilhas CPI profundas sem simulação
Sumidouros de tamanho de binário:
- Grafos de dependência pesados
- Programas monolíticos "um crate faz tudo"
- Código só de debug deixado em builds release
Disciplina significa early returns, iteração limitada, deps enxutas e profiling com simulação antes de tráfego mainnet-beta.
Considerações Avançadas e Aplicações
Escolhendo uma superfície Rust
| Abordagem | Experiência Rust | CU / tamanho | Quando encaixa |
|---|---|---|---|
| Anchor 0.32.1 | Macros, contas, IDL | Baseline maior | Velocidade de produto, clientes tipados |
solana-program nativo | Entrypoint e checagens explícitas | Teto menor se cuidadoso | Aprendizado, loaders custom de controle |
| Pinocchio / pilhas finas | Orientado a sistemas | Geralmente mais enxuto | Caminhos quentes, programas de infraestrutura |
| Híbrido | Camada app Anchor + core fino | Split por CPI | Sistemas maduros com hotspots medidos |
Os três ainda obedecem restrições no_std, layouts Borsh-ou-bytes, matemática checked e metering de CU. Escolha de framework muda macros e ergonomia de validação, não as regras do SVM.
Mapeando intuição Rust de host para trabalho on-chain
| Hábito Rust de host | Substituto on-chain |
|---|---|
main + args CLI | Entrypoint + bytes de instrução + lista de contas |
| Arquivos / linhas de DB | Contas owned pelo programa com layouts fixos ou versionados |
anyhow + stack traces | Códigos compactos ProgramError + logs seletivos |
Crescimento ilimitado de Vec | Capacidade fixa, storage em conta, contagens máximas |
| Threads / async | Instrução single-thread; paralelização é Sealevel entre txs |
println! | msg! (medido) |
| Dinheiro em ponto flutuante | Lamports inteiros / unidades base de token + ops checked |
Padrões e testes
Use headers de conta versionados, discriminators (tags manuais ou de 8 bytes do Anchor), seeds PDA como parte da API, e helpers CPI que ainda exigem contas declaradas. Mantenha testes só de host atrás de cfg para não inchar o binário sBPF.
Teste unitariamente matemática pura e packing no host. Rode instruções completas sob LiteSVM 0.6.x ou Surfpool 0.12.0. Meça tamanho de binário após build e computeUnitsConsumed da simulação (meta RPC Kit basta para checagens no lado do cliente).
Equívocos Comuns
- "Se eu sei Rust, programas Solana são só outro crate." O target, entrypoint, modelo de conta e metering mudam quais crates e padrões são válidos.
- "Posso usar a biblioteca padrão como um backend." On-chain é
no_stdcomallocopcional; a maioria destde pilhas async não compila para sBPF. - "Anchor remove a necessidade de entender Borsh e contas." Anchor gera código sobre os mesmos bytes, owners e discriminators; layouts ruins ainda quebram migrações.
- "Builds release tornam overflow de inteiro seguro automaticamente." Lógica financeira deve usar APIs checked ou saturating deliberadamente.
- "
unwrapé ok se clientes são honestos." Clientes são adversariais; panics desperdiçam CU e produzem UX de cliente pior que erros tipados. - "Mais logging sempre ajuda em produção." Excesso de
msg!pode empurrar caminhos além do orçamento de CU; prefira erros e indexação off-chain para eventos de alto volume. - "Dependências maiores só prejudicam tempo de compile." Podem inflar tamanho de deploy e puxar mais código para caminhos quentes; audite deps como risco de produção.
- "Zero-copy é sempre melhor que Borsh." Zero-copy precisa de regras estritas
repr(C)/Pod; campos de comprimento variável costumam favorecer Borsh. - "Upgrades de programa reescrevem dados de conta de graça." Upgrades trocam bytecode; bytes de conta ficam até você migrá-los.
FAQs
O que Rust compila para programas Solana?
Bytecode sBPF (ELF .so) executado pelo SVM em validadores Agave. Você builda com cargo build-sbf ou build do Anchor, não só um target de host normal.
Por que não há função `main`?
O loader BPF/SBF chama o entrypoint registrado para cada instrução. Frameworks geram ou envolvem esse entrypoint para você.
Qual a diferença prática entre no_std e "alloc limitado"?
no_std remove a biblioteca padrão. alloc restaura opcionalmente tipos de heap (Vec, String) via allocator do programa, ainda sem I/O de SO ou threads. Prefira layouts fixos quando possível.
Anchor ainda é Rust on-chain "de verdade"?
Sim. Anchor 0.32.1 emite Rust que aponta para o mesmo runtime. Macros reduzem boilerplate para contas, erros e IDL; não mudam regras Sealevel.
Por que Borsh é tão comum?
É determinístico, compacto e compartilhado entre programas nativos, padrões Anchor e muitos coders de cliente. Estabilidade cross-language importa mais que usar qualquer formato serde que você gosta no host.
Quando devo trocar de Borsh para bytemuck?
Quando contas são grandes, de tamanho fixo e quentes o bastante para deserialize/serialize completo dominar CU. Mantenha regras de alinhamento e padding sob revisão.
Preciso de matemática checked se uso só quantidades u64 de token?
Sim. Adição, multiplicação para taxas e matemática de shares ainda podem estourar u64. Ops checked transformam isso em falha limpa de instrução.
Como clientes devem ler falhas de programa?
Mapeie códigos de erro custom no programa e decodifique off-chain (tabelas IDL/erro com Anchor, ou códigos documentados para nativo). Use meta de transação @solana/kit 7.0.0 para logs e campos err.
Qual um bom primeiro passe de otimização de CU?
Remova logs em caminho quente, limite loops, evite clonar buffers grandes, deserialize só campos necessários e simule com tamanhos de conta realistas.
Como sei se um crate é seguro para depender?
Deve compilar sob cargo build-sbf, de preferência como no_std. Cheque deps transitivas por features só std e prefira crates mantidos para programas Solana.
Posso usar ponto flutuante on-chain?
Prefira fixed-point inteiro para saldos, taxas e shares. Evite floats para qualquer coisa financeira.
Como esta seção se relaciona com docs de runtime Sealevel?
Páginas Sealevel cobrem locks, paralelismo e metering. Esta seção cobre o subconjunto Rust que você escreve dentro desse runtime.
Quais versões minha equipe deve fixar?
Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0, LiteSVM 0.6.x.
Por onde começo se sou novo em Rust on-chain?
Este overview, depois Rust for Solana Basics, depois páginas no_std, Borsh, aritmética, erros e CU conforme você constrói.
Pinocchio muda o modelo?
Ele afina deps e estilo de entrypoint. Você ainda raciocina sobre contas, bytes, erros e CU da mesma forma.
Relacionado
- Rust for Solana Basics - exemplos curtos de entrypoint, no_std, Borsh e ops checked
- no_std & the Program Environment - o que o sandbox permite e proíbe
- Borsh Serialization - codificação de instrução e conta em profundidade
- Safe Arithmetic - padrões checked e saturating para saldos e taxas
- Error Handling On-Chain - ProgramError, códigos custom e
? - Program Size & CU Discipline - tamanho de binário e orçamentos de compute
Versões da stack: Esta página foi escrita para Agave 4.1.1, Solana CLI 3.0.10, Anchor 0.32.1, anchor-lang 0.32.1, Rust 1.91.1, @solana/kit 7.0.0, Surfpool 0.12.0 e LiteSVM 0.6.x.