Resumo
Os programas são compilados para sBPF via LLVM e executados numa VM isolada com um orçamento de 1,4M CU por transação. O runtime mantém em cache até 512 programas compilados, fornece syscalls para logging, CPI, criptografia e memória, e atrasa novas implementações em 1 slot.
Compilação
A Solana usa LLVM para compilar programas em binários ELF contendo Solana Bytecode Format (sBPF). O binário ELF é armazenado on-chain 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 de duas abordagens:
Anchor
Uma framework que usa macros Rust para reduzir código repetitivo. Recomendada para a maioria dos programadores.
Rust nativo
Rust direto sem frameworks. Oferece controlo total mas requer mais implementação manual.
Modelo de execução de programas
Quando uma transação é processada, o runtime executa cada instrução
sequencialmente através 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 escrita, e configurar oTransactionContext. -
Verifica precompilações. Se o programa for uma precompilação, o runtime chama
process_precompile(), que ainda adiciona e remove um stack frame (viapush()epop()) mas ignora a VM sBPF e a pesquisa na cache de programas, executando código nativo diretamente. -
Empurra um stack frame. (Os passos 3-6 acontecem dentro de
InvokeContext::process_instruction()eprocess_executable_chain(), chamados deprocess_message().) Chamapush()emInvokeContext, que incrementa a altura da pilha de instruções e aplica a regra de reentrada: um programa só pode reentrar em si mesmo se o chamador imediato (o programa no topo atual da pilha de instruções) for o mesmo programa. Auto-recursão profunda (A -> A -> A) é permitida, sujeita aos limites de profundidade da pilha. Outros padrões de reentrada (por exemplo, A chama B chama A) retornamInstructionError::ReentrancyNotAllowed. -
Resolve o programa. O runtime chama
process_executable_chain()que determina o loader. Se o proprietário da conta do programa for o native loader, o programa é um builtin e sua função de entrypoint é procurada diretamente noProgramCacheForTxBatch. Se o proprietário for um dos loaders BPF (bpf_loader_deprecated,bpf_loader,bpf_loader_upgradeable, ouloader_v4), o entrypoint builtin do próprio loader é invocado. -
Executa o programa BPF. Para programas BPF, o entrypoint do loader procura 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 para o estado da conta
-
Remove o stack frame. Chama
pop()que verifica se a instrução não violou as regras de contabilidade do runtime (saldos de lamport estão equilibrados, contas somente leitura não foram modificadas, tamanhos de dados de conta 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 reconhece o grafo de
bifurcações e lida com regras de visibilidade de implantação, remoção e
recompilação no limite de epoch.
Tipos de entrada de 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 validador (System, Stake, Vote, etc.). Não armazenado on-chain. |
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 | Marcador para programas que não passaram no verificador sBPF sob o conjunto de recursos atual. Pode tornar-se Loaded se as ativações de recursos alterarem as regras de verificação. |
Closed | Marcador para programas que foram explicitamente fechados ou nunca implantados. Também usado para contas (como contas de buffer) que pertencem a um carregador mas não contêm código executável. |
DelayVisibility | Marcador sintético retornado por ProgramCacheForTxBatch::find() quando uma entrada Loaded existe mas ainda não está efetiva (seu effective_slot está no futuro). Nunca armazenado diretamente no cache. |
Atraso de visibilidade
Programas recém-implantados ou atualizados não são efetivos imediatamente. A
constante
DELAY_VISIBILITY_SLOT_OFFSET
é 1, o que significa que um programa implantado no slot N torna-se efetivo 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
"Programa não está implantado."
Política de remoção
O cache armazena 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 o
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 através da syscall
sol_set_return_data. Os dados são armazenados numa estrutura
TransactionReturnData
ao nível da 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?