Esecuzione dei programmi

Riepilogo

I programmi vengono compilati in sBPF tramite LLVM ed eseguiti in una VM isolata con un budget di 1,4M CU per transazione. Il runtime memorizza nella cache fino a 512 programmi compilati, fornisce syscall per logging, CPI, crittografia e memoria, e ritarda i nuovi deployment di 1 slot.

Compilazione

Solana utilizza LLVM per compilare i programmi in binari ELF contenenti il Solana Bytecode Format (sBPF). Il binario ELF viene memorizzato onchain in un account eseguibile.

sBPF è la variante personalizzata di Solana del bytecode eBPF, adattata per il runtime di Solana. Non è eBPF standard e presenta modifiche specifiche per Solana.

Scrivi programmi

I programmi Solana sono scritti principalmente in Rust utilizzando uno dei tre approcci seguenti:

Consulta Cross-Program Invocations per i concetti e gli esempi di CPI applicabili a questi approcci.

Modello di esecuzione dei programmi

Quando una transazione viene elaborata, il runtime esegue ogni istruzione in sequenza tramite process_message(). Per ogni istruzione, il runtime:

  1. Prepara il contesto dell'istruzione. Chiama prepare_next_top_level_instruction() per mappare gli indici degli account dell'istruzione, impostare i flag di firmatario e scrivibile, e configurare il TransactionContext.

  2. Verifica i precompilati. Se il programma è un precompilato, il runtime chiama process_precompile(), che inserisce e rimuove comunque uno stack frame (tramite push() e pop()) ma bypassa la VM sBPF e la ricerca nella cache dei programmi, eseguendo il codice nativo direttamente.

  3. Inserisce uno stack frame. (I passaggi 3-6 avvengono all'interno di InvokeContext::process_instruction() e process_executable_chain(), chiamati da process_message().) Chiama push() su InvokeContext, che incrementa l'altezza dello stack delle istruzioni e applica la regola di rientranza: un programma può rientrare in se stesso solo se il chiamante immediato (il programma in cima all'attuale stack delle istruzioni) è lo stesso programma. La ricorsione profonda (A -> A -> A) è consentita, nel rispetto dei limiti di profondità dello stack. Altri pattern di rientranza (es. A chiama B che chiama A) restituiscono InstructionError::ReentrancyNotAllowed.

  4. Risolve il programma. Il runtime chiama process_executable_chain() che determina il loader. Se il proprietario del program account è il native loader, il programma è un builtin e la sua funzione entrypoint viene recuperata direttamente dal ProgramCacheForTxBatch. Se il proprietario è uno dei loader BPF (bpf_loader_deprecated, bpf_loader, bpf_loader_upgradeable o loader_v4), viene invocato invece l'entrypoint builtin del loader stesso.

  5. Esegue il programma BPF. Per i programmi BPF, il loader entrypoint recupera l'eseguibile compilato dalla cache dei programmi. La funzione execute() quindi:

    • Serializza i dati degli account in un buffer di parametri piatto
    • Crea la VM sBPF con stack, heap e regioni di memoria
    • Esegue il codice compilato, consumando unità di calcolo durante l'esecuzione. Restituisce ComputationalBudgetExceeded se il budget viene superato.
    • Deserializza i dati degli account dal buffer ripristinando lo stato degli account
  6. Rimuove lo stack frame. Chiama pop() che verifica che l'istruzione non abbia violato le regole di contabilità del runtime (i saldi lamport sono bilanciati, gli account in sola lettura non sono stati modificati, le dimensioni dei dati degli account rientrano nei limiti).

  7. Accumula le unità di calcolo. Le unità di calcolo consumate dall'istruzione vengono aggiunte al totale della transazione tramite saturating_add.

Cache dei programmi

Il runtime mantiene un ProgramCache globale che memorizza i programmi verificati e compilati. È consapevole del fork-graph e gestisce le regole di visibilità del deployment, l'eviction e la ricompilazione al confine di epoch.

Tipi di voce nella cache

Ogni programma memorizzato nella cache ha un ProgramCacheEntryType che determina il suo comportamento a runtime:

TipoDescrizione
LoadedProgramma verificato e compilato, pronto per l'esecuzione.
BuiltinProgramma nativo compilato nel binario del validator (System, Stake, Vote, ecc.). Non memorizzato onchain.
UnloadedProgramma precedentemente verificato il cui eseguibile compilato è stato rimosso dalla memoria per liberare spazio. Mantiene ancora le statistiche di utilizzo. Può essere ricaricato senza una nuova verifica.
FailedVerificationSegnaposto per i programmi che non hanno superato il verificatore sBPF con il set di funzionalità attuale. Potrebbe diventare Loaded se le attivazioni di funzionalità modificano le regole di verifica.
ClosedSegnaposto per i programmi chiusi esplicitamente o mai distribuiti. Utilizzato anche per account (come i buffer account) che appartengono a un loader ma non contengono codice eseguibile.
DelayVisibilitySegnaposto sintetico restituito da ProgramCacheForTxBatch::find() quando esiste una voce Loaded ma non è ancora efficace (il suo effective_slot è nel futuro). Non viene mai memorizzato direttamente nella cache.

Ritardo di visibilità

I programmi appena distribuiti o aggiornati non diventano effettivi immediatamente. La costante DELAY_VISIBILITY_SLOT_OFFSET è 1, il che significa che un programma distribuito nello slot N diventa effettivo nello slot N+1. Durante lo slot di deployment, qualsiasi tentativo di invocare la nuova versione restituisce DelayVisibility, causando la segnalazione "Program is not deployed." da parte del runtime.

Policy di eviction

La cache contiene fino a MAX_LOADED_ENTRY_COUNT (512) voci di programmi compilati. Quando il limite viene raggiunto, i programmi meno utilizzati vengono rimossi allo stato Unloaded. L'utilizzo è tracciato da tx_usage_counter (incrementato ogni volta che una transazione fa riferimento al programma) e latest_access_slot.

Ricompilazione al confine di epoch

Se l'attivazione di una funzionalità modifica i ProgramRuntimeEnvironments al confine di un epoch, tutti i programmi nella cache vengono ricompilati contro il nuovo ambiente.

Dati di ritorno

I programmi possono impostare i dati di ritorno tramite la syscall sol_set_return_data. I dati vengono memorizzati in una struct TransactionReturnData a livello di transazione che contiene i byte dei dati e il program_id del programma la cui istruzione ha chiamato la syscall. La dimensione massima è 1.024 byte (MAX_RETURN_DATA).

Is this page helpful?

Indice dei contenuti

Modifica pagina
© 2026 Solana Foundation. Tutti i diritti riservati.