Exécution des programmes

Résumé

Les programmes sont compilés en sBPF via LLVM et s'exécutent dans une VM isolée avec un budget de 1,4 M d'UC 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'1 slot.

Compilation

Solana utilise LLVM pour compiler les programmes en binaires ELF contenant le format de bytecode Solana (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 au runtime Solana. Il ne s'agit pas d'eBPF standard et il comporte des modifications spécifiques à Solana.

Écrire des programmes

Les programmes Solana sont principalement écrits en Rust selon l'une des trois approches suivantes :

Consultez Cross-Program Invocations pour les concepts et exemples de CPI qui s'appliquent à ces différentes approches.

Modèle d'exécution des programmes

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 de l'instruction. Appelle prepare_next_top_level_instruction() pour mapper les indices de comptes 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 pousse et dépile quand même un cadre de pile (via push() et pop()) mais contourne la VM sBPF et la recherche dans le cache de programmes, en exécutant le code natif directement.

  3. Pousse un cadre de pile. (Les étapes 3 à 6 se déroulent dans InvokeContext::process_instruction() et process_executable_chain(), appelés depuis process_message().) Appelle push() sur InvokeContext, ce qui incrémente la hauteur de la pile d'instructions et applique la règle de réentrance : un programme ne peut se réentrer 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 motifs de réentrance (par ex., A appelle B qui appelle A) retournent InstructionError::ReentrancyNotAllowed.

  4. Résout le programme. Le runtime appelle process_executable_chain() qui détermine le chargeur. Si le propriétaire du program account est le chargeur natif, le programme est un builtin et sa fonction 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 propre au 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 :

    • Sérialise les données des comptes 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. Retourne ComputationalBudgetExceeded si le budget est dépassé.
    • Désérialise les données des comptes du tampon vers l'état des comptes
  6. Dépile le cadre de pile. Appelle pop() qui vérifie que l'instruction n'a pas violé les règles de comptabilité du runtime (les soldes en lamport sont équilibrés, les comptes en lecture seule n'ont pas été modifiés, les tailles des données des comptes sont dans les limites autorisées).

  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 programmes

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

Types d'entrées du cache

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

TypeDescription
LoadedProgramme vérifié et compilé, prêt à l'exécution.
BuiltinProgramme natif compilé dans le binaire du validator (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 les statistiques d'utilisation. Peut être rechargé sans re-vérification.
FailedVerificationMarqueur de suppression pour les programmes n'ayant pas passé le vérificateur sBPF avec l'ensemble de fonctionnalités actuel. Peut devenir Loaded si des activations de fonctionnalités modifient les règles de vérification.
ClosedMarqueur de suppression pour les programmes explicitement fermés ou jamais déployés. Utilisé également pour les comptes (tels que les comptes tampon) appartenant à un chargeur mais ne contenant pas de code exécutable.
DelayVisibilityMarqueur de suppression 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 à jour ne sont pas effectifs immédiatement. La constante DELAY_VISIBILITY_SLOT_OFFSET vaut 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 peut contenir 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 une activation de fonctionnalité modifie les ProgramRuntimeEnvironments à une limite d'epoch, tous les programmes en cache sont recompilés par rapport au nouvel environnement.

Données de retour

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

Is this page helpful?

Table des matières

Modifier la page
© 2026 Fondation Solana. Tous droits réservés.