Programmausführung

Zusammenfassung

Programme werden über LLVM zu sBPF kompiliert und in einer isolierten VM mit einem Budget von 1,4 Mio. CU pro Transaktion ausgeführt. Die Laufzeit speichert bis zu 512 kompilierte Programme im Cache, stellt Syscalls für Logging, CPI, Kryptografie und Speicher bereit und verzögert neue Deployments um 1 slot.

Kompilierung

Solana verwendet LLVM, um Programme in ELF-Binärdateien zu kompilieren, die das Solana Bytecode Format (sBPF) enthalten. Die ELF-Binärdatei wird onchain in einem ausführbaren Konto gespeichert.

sBPF ist Solanas benutzerdefinierte Variante von eBPF-Bytecode, angepasst für die Solana-Laufzeit. Es handelt sich nicht um Standard-eBPF und enthält Solana-spezifische Modifikationen.

Programme schreiben

Solana-Programme werden hauptsächlich in Rust mit einem von drei Ansätzen geschrieben:

Siehe Cross-Program Invocations für CPI-Konzepte und Beispiele, die für alle diese Ansätze gelten.

Programmausführungsmodell

Bei der Verarbeitung einer Transaktion führt die Laufzeit jede Anweisung sequenziell über process_message() aus. Für jede Anweisung führt die Laufzeit Folgendes durch:

  1. Bereitet den Anweisungskontext vor. Ruft prepare_next_top_level_instruction() auf, um die Kontoindizes der Anweisung zuzuordnen, Signer- und Schreibberechtigungen zu setzen und den TransactionContext zu konfigurieren.

  2. Prüft Precompiles. Wenn das Programm ein Precompile ist, ruft die Laufzeit process_precompile() auf, das zwar noch einen Stack-Frame schiebt und entfernt (über push() und pop()), jedoch die sBPF-VM und den Programm-Cache-Lookup umgeht und nativen Code direkt ausführt.

  3. Schiebt einen Stack-Frame. (Schritte 3–6 finden innerhalb von InvokeContext::process_instruction() und process_executable_chain(), aufgerufen von process_message(), statt.) Ruft push() auf InvokeContext auf, was die Anweisungs-Stack-Höhe erhöht und die Reentrant-Regel durchsetzt: Ein Programm darf sich nur selbst erneut aufrufen, wenn der unmittelbare Aufrufer (das Programm am aktuellen oberen Ende des Anweisungs-Stacks) dasselbe Programm ist. Tiefe Selbstrekursion (A -> A -> A) ist erlaubt, unterliegt jedoch Stack-Tiefenbeschränkungen. Andere Reentrant-Muster (z. B. A ruft B auf, B ruft A auf) geben InstructionError::ReentrancyNotAllowed zurück.

  4. Löst das Programm auf. Die Laufzeit ruft process_executable_chain() auf, das den Loader bestimmt. Wenn der Eigentümer des Programmkontos der native loader ist, ist das Programm ein Builtin und sein Einstiegspunkt wird direkt aus dem ProgramCacheForTxBatch nachgeschlagen. Wenn der Eigentümer einer der BPF-Loader ist (bpf_loader_deprecated, bpf_loader, bpf_loader_upgradeable oder loader_v4), wird stattdessen der eigene Builtin-Einstiegspunkt des Loaders aufgerufen.

  5. Führt das BPF-Programm aus. Für BPF-Programme sucht der Loader-Einstiegspunkt das kompilierte Executable aus dem Programm-Cache. Die execute()-Funktion führt dann Folgendes durch:

    • Serialisiert Kontodaten in einen flachen Parameterpuffer
    • Erstellt die sBPF-VM mit Stack, Heap und Speicherbereichen
    • Führt den kompilierten Code aus und verbraucht dabei Compute Units. Gibt ComputationalBudgetExceeded zurück, wenn das Budget überschritten wird.
    • Deserialisiert Kontodaten aus dem Puffer zurück in den Kontostatus
  6. Entfernt den Stack-Frame. Ruft pop() auf, das überprüft, ob die Anweisung die Abrechnungsregeln der Laufzeit nicht verletzt hat (Lamport-Salden sind ausgeglichen, schreibgeschützte Konten wurden nicht geändert, Kontodatengrößen liegen innerhalb der Grenzen).

  7. Akkumuliert Compute Units. Die von der Anweisung verbrauchten Compute Units werden über saturating_add zum Transaktionsgesamtwert addiert.

Programm-Cache

Die Laufzeit pflegt einen globalen ProgramCache, der verifizierte und kompilierte Programme speichert. Er ist fork-graph-bewusst und verwaltet Deployment-Sichtbarkeitsregeln, Verdrängung und Neukompilierung an Epoch-Grenzen.

Cache-Eintragstypen

Jedes gecachte Programm hat einen ProgramCacheEntryType, der sein Laufzeitverhalten bestimmt:

TypBeschreibung
LoadedVerifiziertes und kompiliertes Programm, bereit zur Ausführung.
BuiltinNatives Programm, das in die Validatoren-Binärdatei kompiliert ist (System, Stake, Vote usw.). Wird nicht onchain gespeichert.
UnloadedZuvor verifiziertes Programm, dessen kompiliertes Executable aus dem Speicher verdrängt wurde, um Platz freizugeben. Verfolgt weiterhin Nutzungsstatistiken. Kann ohne erneute Verifizierung neu geladen werden.
FailedVerificationTombstone für Programme, die den sBPF-Verifizierer unter dem aktuellen Feature-Set nicht bestanden haben. Kann zu Loaded werden, wenn Feature-Aktivierungen die Verifizierungsregeln ändern.
ClosedTombstone für Programme, die explizit geschlossen oder nie deployed wurden. Wird auch für Konten verwendet (wie Buffer-Accounts), die zu einem Loader gehören, aber keinen ausführbaren Code enthalten.
DelayVisibilitySynthetischer Tombstone, der von ProgramCacheForTxBatch::find() zurückgegeben wird, wenn ein Loaded-Eintrag existiert, aber noch nicht wirksam ist (sein effective_slot liegt in der Zukunft). Wird nie direkt im Cache gespeichert.

Sichtbarkeitsverzögerung

Neu deployede oder aktualisierte Programme sind nicht sofort wirksam. Die DELAY_VISIBILITY_SLOT_OFFSET-Konstante hat den Wert 1, was bedeutet, dass ein in slot N deployedes Programm in slot N+1 wirksam wird. Während des Deployment-Slots gibt jeder Versuch, die neue Version aufzurufen, DelayVisibility zurück, wodurch die Laufzeit "Program is not deployed." meldet.

Verdrängungsrichtlinie

Der Cache enthält bis zu MAX_LOADED_ENTRY_COUNT (512) kompilierte Programmeinträge. Wenn das Limit erreicht ist, werden die am wenigsten genutzten Programme in den Unloaded-Zustand verdrängt. Die Nutzung wird durch tx_usage_counter (wird jedes Mal erhöht, wenn eine Transaktion das Programm referenziert) und latest_access_slot verfolgt.

Neukompilierung an Epoch-Grenzen

Wenn eine Feature-Aktivierung die ProgramRuntimeEnvironments an einer Epoch-Grenze ändert, werden alle gecachten Programme neu kompiliert gegen die neue Umgebung.

Rückgabedaten

Programme können Rückgabedaten über den sol_set_return_data-Syscall setzen. Die Daten werden in einem transaktionsweiten TransactionReturnData-Struct gespeichert, das die Datenbytes und die program_id des Programms enthält, dessen Anweisungen den Syscall aufgerufen haben. Die maximale Größe beträgt 1.024 Bytes (MAX_RETURN_DATA).

Is this page helpful?

Inhaltsverzeichnis

Seite bearbeiten
© 2026 Solana Foundation. Alle Rechte vorbehalten.