Exécution de programme

Résumé

Les programmes se compilent en sBPF via LLVM et s'exécutent dans une VM isolée avec un budget de 1,4M CU par transaction. Le runtime met en cache jusqu'à 512 programmes compilés, fournit des appels système pour la journalisation, les CPI, la cryptographie et la mémoire, et retarde les nouveaux déploiements d'un slot.

Compilation

Solana utilise LLVM pour compiler les programmes en binaires ELF contenant le Solana Bytecode Format (sBPF). Le binaire ELF est stocké on-chain dans un compte exécutable.

sBPF est la variante personnalisée de Solana du bytecode eBPF, adaptée pour le runtime Solana. Ce n'est pas de l'eBPF standard et comporte des modifications spécifiques à Solana.

Écrire des programmes

Les programmes Solana sont principalement écrits en Rust en utilisant l'une des deux approches :

Modèle d'exécution de programme

Lorsqu'une transaction est traitée, le runtime exécute chaque instruction séquentiellement via process_message(). Pour chaque instruction, le runtime :

  1. Prépare le contexte d'instruction. Appelle prepare_next_top_level_instruction() pour mapper les indices de compte de l'instruction, définir les indicateurs de signataire et d'écriture, et configurer le TransactionContext.

  2. Vérifie les précompilations. Si le programme est une précompilation, le runtime appelle process_precompile(), qui empile et dépile toujours une frame de pile (via push() et pop()) mais contourne la VM sBPF et la recherche dans le cache de programmes, exécutant le code natif directement.

  3. Pousse une trame de pile. (Les étapes 3 à 6 se produisent dans InvokeContext::process_instruction() et process_executable_chain(), appelées depuis process_message().) Appelle push() sur InvokeContext, qui incrémente la hauteur de la pile d'instructions et applique la règle de réentrance : un programme ne peut se réappeler lui-même que si l'appelant immédiat (le programme au sommet actuel de la pile d'instructions) est le même programme. La récursion profonde (A -> A -> A) est autorisée, sous réserve des limites de profondeur de pile. Les autres schémas de réentrance (par exemple, A appelle B qui appelle A) renvoient InstructionError::ReentrancyNotAllowed.

  4. Résout le programme. Le runtime appelle process_executable_chain() qui détermine le chargeur. Si le propriétaire du compte de programme est le chargeur natif, le programme est un builtin et sa fonction de point d'entrée est recherchée directement dans le ProgramCacheForTxBatch. Si le propriétaire est l'un des chargeurs BPF (bpf_loader_deprecated, bpf_loader, bpf_loader_upgradeable, ou loader_v4), le point d'entrée builtin du chargeur est invoqué à la place.

  5. Exécute le programme BPF. Pour les programmes BPF, le point d'entrée du chargeur recherche l'exécutable compilé dans le cache de programmes. La fonction execute() effectue ensuite les opérations suivantes :

    • Sérialise les données de compte dans un tampon de paramètres plat
    • Crée la VM sBPF avec les régions de pile, de tas et de mémoire
    • Exécute le code compilé, en consommant des unités de calcul pendant l'exécution. Renvoie ComputationalBudgetExceeded si le budget est dépassé.
    • Désérialise les données de compte du tampon vers l'état du compte
  6. Dépile la trame de pile. Appelle pop() qui vérifie que l'instruction n'a pas violé les règles comptables du runtime (les soldes en lamports sont équilibrés, les comptes en lecture seule n'ont pas été modifiés, les tailles de données de compte sont dans les limites).

  7. Accumule les unités de calcul. Les unités de calcul consommées par l'instruction sont ajoutées au total de la transaction via saturating_add.

Cache de programme

Le runtime maintient un ProgramCache global qui stocke les programmes vérifiés et compilés. Il est conscient du graphe de fork et gère les règles de visibilité de déploiement, l'éviction et la recompilation aux limites d'epoch.

Types d'entrées de cache

Chaque programme en cache possède un ProgramCacheEntryType qui détermine son comportement à l'exécution :

TypeDescription
LoadedProgramme vérifié et compilé, prêt pour l'exécution.
BuiltinProgramme natif compilé dans le binaire du validateur (System, Stake, Vote, etc.). Non stocké on-chain.
UnloadedProgramme précédemment vérifié dont l'exécutable compilé a été évincé de la mémoire pour libérer de l'espace. Conserve toujours les statistiques d'utilisation. Peut être rechargé sans revérification.
FailedVerificationMarqueur pour les programmes qui n'ont pas passé le vérificateur sBPF sous l'ensemble de fonctionnalités actuel. Peut devenir Loaded si les activations de fonctionnalités modifient les règles de vérification.
ClosedMarqueur pour les programmes qui ont été explicitement fermés ou jamais déployés. Également utilisé pour les comptes (tels que les comptes tampons) qui appartiennent à un chargeur mais ne contiennent pas de code exécutable.
DelayVisibilityMarqueur synthétique retourné par ProgramCacheForTxBatch::find() lorsqu'une entrée Loaded existe mais n'est pas encore effective (son effective_slot est dans le futur). Jamais stocké directement dans le cache.

Délai de visibilité

Les programmes nouvellement déployés ou mis à niveau ne sont pas effectifs immédiatement. La constante DELAY_VISIBILITY_SLOT_OFFSET est 1, ce qui signifie qu'un programme déployé dans le slot N devient effectif dans le slot N+1. Pendant le slot de déploiement, toute tentative d'invoquer la nouvelle version retourne DelayVisibility, ce qui amène le runtime à signaler « Le programme n'est pas déployé ».

Politique d'éviction

Le cache contient jusqu'à MAX_LOADED_ENTRY_COUNT (512) entrées de programmes compilés. Lorsque la limite est atteinte, les programmes les moins utilisés sont évincés vers l'état Unloaded. L'utilisation est suivie par tx_usage_counter (incrémenté à chaque fois qu'une transaction référence le programme) et latest_access_slot.

Recompilation à la limite d'epoch

Si l'activation d'une fonctionnalité modifie le ProgramRuntimeEnvironments à une limite d'epoch, tous les programmes en cache sont recompilés contre le nouvel environnement.

Données de retour

Les programmes peuvent définir des données de retour via le syscall sol_set_return_data. Les données sont stockées dans une structure TransactionReturnData au niveau de la transaction qui contient les octets de données et le program_id du programme dont l'instruction a appelé le syscall. La taille maximale est de 1 024 octets (MAX_RETURN_DATA).

Is this page helpful?

Table des matières

Modifier la page

Géré par

© 2026 Fondation Solana.
Tous droits réservés.
Restez connecté