Resumo
Os programas são compilados para sBPF via LLVM e executados numa VM isolada com um orçamento de 1,4 M CU por transação. O runtime armazena em cache até 512 programas compilados, fornece syscalls para logging, CPI, criptografia e memória, e atrasa novos deployments em 1 slot.
Compilação
A Solana usa LLVM para compilar programas em binários ELF contendo o Solana Bytecode Format (sBPF). O binário ELF é armazenado onchain numa conta executável.
sBPF é a variante personalizada da Solana do bytecode eBPF, adaptada para o runtime da Solana. Não é eBPF padrão e possui modificações específicas da Solana.
Escrever programas
Os programas Solana são escritos principalmente em Rust usando uma das três abordagens:
Anchor
Um framework que utiliza macros Rust para reduzir o código repetitivo. Recomendado para a maioria dos desenvolvedores.
Pinocchio
Uma biblioteca Rust leve e zero-copy otimizada para consumo de computação e tamanho do binário. Inclui crates específicos de programas para CPIs comuns.
Rust Nativo
Rust direto sem frameworks. Oferece controle total, mas requer mais implementação manual.
Consulte Cross-Program Invocations para conceitos e exemplos de CPI que se aplicam a todas essas abordagens.
Modelo de execução de programas
Quando uma transação é processada, o runtime executa cada instrução
sequencialmente por meio de
process_message().
Para cada instrução, o runtime:
-
Prepara o contexto da instrução. Chama
prepare_next_top_level_instruction()para mapear os índices de conta da instrução, definir flags de signatário e de escrita, e configurar oTransactionContext. -
Verifica os precompiles. Se o programa for um precompile, o runtime chama
process_precompile(), que ainda empurra e retira um frame de pilha (viapush()epop()) mas ignora a VM sBPF e a busca no cache de programas, executando código nativo diretamente. -
Empurra um frame de pilha. (Os passos 3-6 ocorrem dentro de
InvokeContext::process_instruction()eprocess_executable_chain(), chamados a partir deprocess_message().) Chamapush()emInvokeContext, que incrementa a altura da pilha de instruções e aplica a regra de reentrância: um programa só pode reingressar em si mesmo se o chamador imediato (o programa no topo atual da pilha de instruções) for o mesmo programa. A autorrecursão profunda (A -> A -> A) é permitida, sujeita aos limites de profundidade de pilha. Outros padrões de reentrância (ex.: A chama B que chama A) retornamInstructionError::ReentrancyNotAllowed. -
Resolve o programa. O runtime chama
process_executable_chain()que determina o loader. Se o proprietário do program account for o native loader, o programa é um builtin e sua função de entrypoint é consultada diretamente doProgramCacheForTxBatch. Se o proprietário for um dos BPF loaders (bpf_loader_deprecated,bpf_loader,bpf_loader_upgradeableouloader_v4), o próprio entrypoint builtin do loader é invocado. -
Executa o programa BPF. Para programas BPF, o entrypoint do loader busca o executável compilado no cache de programas. A função
execute()então:- Serializa os dados da conta num buffer de parâmetros plano
- Cria a VM sBPF com regiões de pilha, heap e memória
- Executa o código compilado, consumindo unidades de computação durante a execução. Retorna
ComputationalBudgetExceededse o orçamento for excedido. - Desserializa os dados da conta do buffer de volta ao estado da conta
-
Retira o frame de pilha. Chama
pop()que verifica se a instrução não violou as regras de contabilidade do runtime (os saldos de lamport estão equilibrados, as contas somente leitura não foram modificadas, os tamanhos dos dados das contas estão dentro dos limites). -
Acumula unidades de computação. As unidades de computação consumidas pela instrução são adicionadas ao total da transação via
saturating_add.
Cache de programas
O runtime mantém um
ProgramCache
global que armazena programas verificados e compilados. Ele é ciente do fork-graph e lida com
regras de visibilidade de deployment, evicção e recompilação no limite de epoch.
Tipos de entrada no cache
Cada programa em cache possui um
ProgramCacheEntryType
que determina seu comportamento em tempo de execução:
| Tipo | Descrição |
|---|---|
Loaded | Programa verificado e compilado, pronto para execução. |
Builtin | Programa nativo compilado no binário do validator (System, Stake, Vote, etc.). Não armazenado onchain. |
Unloaded | Programa previamente verificado cujo executável compilado foi removido da memória para liberar espaço. Ainda rastreia estatísticas de uso. Pode ser recarregado sem reverificação. |
FailedVerification | Tombstone para programas que não passaram no verificador sBPF com o conjunto de funcionalidades atual. Pode tornar-se Loaded se as ativações de funcionalidades alterarem as regras de verificação. |
Closed | Tombstone para programas que foram explicitamente encerrados ou nunca implantados. Também usado para contas (como contas de buffer) que pertencem a um loader mas não contêm código executável. |
DelayVisibility | Tombstone sintético retornado por ProgramCacheForTxBatch::find() quando uma entrada Loaded existe mas ainda não está em vigor (seu effective_slot está no futuro). Nunca armazenado diretamente no cache. |
Atraso de visibilidade
Programas recém-implantados ou atualizados não entram em vigor imediatamente. A
constante DELAY_VISIBILITY_SLOT_OFFSET
é 1, o que significa que um programa implantado no slot N entra em vigor no slot
N+1. Durante o slot de implantação, qualquer tentativa de invocar a nova versão retorna
DelayVisibility, fazendo com que o runtime reporte "Program is not deployed."
Política de evicção
O cache comporta até
MAX_LOADED_ENTRY_COUNT
(512) entradas de programas compilados. Quando o limite é atingido, os programas
menos utilizados são removidos para o estado Unloaded. O uso é rastreado por
tx_usage_counter
(incrementado cada vez que uma transação referencia o programa) e
latest_access_slot.
Recompilação no limite de epoch
Se uma ativação de funcionalidade alterar os
ProgramRuntimeEnvironments
no limite de um epoch, todos os programas em cache são
recompilados
contra o novo ambiente.
Dados de retorno
Os programas podem definir dados de retorno via a syscall sol_set_return_data. Os dados são
armazenados numa
TransactionReturnData
estrutura em nível de transação que contém os bytes de dados e o program_id do programa cuja
instrução chamou a syscall. O tamanho máximo é de 1.024 bytes
(MAX_RETURN_DATA).
Is this page helpful?