
Das Solana-Programmiermodell: Eine Einführung in die Entwicklung auf Solana
Inhaltsverzeichnis
- Worum geht es in diesem Artikel?
- Was sind Solana-Cluster?
- Was sind Accounts?
- Account-Struktur
- Rent
- Adressen auf Solana
- Wie unterscheiden sich Accounts auf Solana von Accounts auf Ethereum?
- Was sind Solana-Programme?
- Was sind Transaktionen?
- Anweisungen
- Was sind versionierte Transaktionen?
- Struktur einer versionierten Transaktion
- Integration des Programmiermodells in den Transaktionsfluss von Solana
- Fazit
- Zusätzliche Ressourcen und weiterführende Literatur
Worum geht es in diesem Artikel?
Solanas Ansatz für dezentrales Computing basiert auf einem einfachen Prinzip: Alles wird in einem eigenen Speicherbereich abgelegt, der als Account bezeichnet wird. Solana fungiert als globaler Key-Value-Store, in dem öffentliche Schlüssel als eindeutige Kennungen für die zugehörigen Accounts dienen. Accounts bilden das Rückgrat von Solana, da sie den Zustand speichern. Sie enthalten alles, von Programmen bis zu Token-Guthaben. Transaktionen aktualisieren Accounts und bilden Zustandsänderungen ab.
In diesem Artikel untersuchen wir die komplexe Architektur von Solana. Zunächst geben wir einen Überblick über Cluster und das Konzept des Zustands. Anschließend besprechen wir die Rolle von Accounts und Programmen als grundlegende Komponenten von Solana. Danach betrachten wir, wie Transaktionen dynamische Interaktionen zwischen Accounts und Programmen ermöglichen.
Am Ende dieses Artikels kennst du das Programmiermodell von Solana genau. Du verstehst die Architektur von Clustern, die zentrale Rolle von Accounts bei der Datenspeicherung und den Prozess, mit dem Transaktionen Account-Daten aktualisieren. Außerdem lernst du Solana-spezifische Funktionen wie das Rent-System und versionierte Transaktionen kennen.
Was sind Solana-Cluster?
Im Zentrum der Solana-Architektur stehen Cluster – eine Gruppe von Validatoren, die gemeinsam Transaktionen verarbeiten und ein einziges Ledger verwalten. Solana besitzt mehrere voneinander getrennte Cluster, die jeweils einem bestimmten Zweck dienen:
- Localhost: ein lokaler Entwicklungs-Cluster am Standardport 8899. Das Solana Command-line Interface (CLI) enthält einen integrierten Test-Validator, den du an deine Anforderungen anpassen kannst, ohne Airdrops zu benötigen oder Rate Limits zu unterliegen
- Devnet: eine Sandbox-Umgebung ohne reale Konsequenzen zum Testen und Experimentieren auf Solana
- Testnet: eine Testumgebung, in der die Core-Mitwirkenden von Solana neue Updates und Funktionen erproben, bevor sie das Mainnet erreichen. Entwickler nutzen sie außerdem für Performance-Tests
- Mainnet Beta: der aktive, erlaubnisfreie Cluster, in dem reale Transaktionen stattfinden. Das ist das „echte“ Solana, auf dem Nutzer, Entwickler, Token-Inhaber und Validatoren täglich interagieren
Jeder Cluster arbeitet unabhängig und hat keinerlei Kenntnis von den anderen. Transaktionen, die an den falschen Cluster gesendet werden, werden abgelehnt. So bleibt die Integrität jeder Betriebsumgebung gewahrt.
Stell dir Cluster als monolithischen Daten-Heap vor. In der Informatik bezeichnet ein Heap einen Speicherbereich, in dem Daten dynamisch gespeichert und verändert werden können. Wichtig ist jedoch: Cluster verwenden nicht buchstäblich eine Heap-Datenstruktur. Diese Analogie soll verdeutlichen, dass Cluster aus verschiedenen Speicherbereichen bestehen, die bei Bedarf zugewiesen und wieder freigegeben werden können. Cluster als dynamischen Heap zu verstehen, ist entscheidend, um nachzuvollziehen, wie Daten im Netzwerk verwaltet, abgerufen und geschützt werden.
Du kannst dir diesen monolithischen Daten-Heap auch als eine Art digitales Lager vorstellen. Daten sind dort wie Kisten in Regalen. Jede besitzt ein eindeutiges Etikett und unterliegt bestimmten Regeln dafür, wie sie bewegt und ihre Inhalte verändert werden dürfen. So entsteht ein sicheres, geordnetes System, in dem nur autorisierte Bewegungen oder Änderungen zulässig sind.
Smart Contracts, die auf Solana Programme heißen, erhalten einen eigenen Bereich des Lagers beziehungsweise Heaps, den sie verwalten können. Ein Programm kann zwar jeden Bereich dieses Lagers lesen, benötigt aber bestimmte Berechtigungen, um Inhalte in einem Bereich zu ändern, der ihm nicht gehört. Die einzige allgemein erlaubte Aktion ist die Übertragung von Lamports, Solanas nativer Kryptowährung, an jeden beliebigen Bereich im Lager.
Der gesamte Zustand liegt in diesem Heap, sogar Programme. Jeder Bereich gehört einem Programm, das ihn entsprechend verwaltet. Programme gehören beispielsweise dem BPFLoader, einem Programm zum Laden, Bereitstellen und Aktualisieren von On-Chain-Programmen. Diese Speicherbereiche, also die Kisten unseres digitalen Lagers, bezeichnen wir als Accounts.
Was sind Accounts?
Alles auf Solana ist ein Account. Stell dir Accounts als Container vor, die Daten dauerhaft speichern, ähnlich wie Dateien auf einem Computer. Sie sind die Bausteine des Solana-Programmiermodells und speichern den Zustand, also beispielsweise Guthaben, Eigentumsinformationen, Angaben dazu, ob der Account ein Programm enthält, sowie Rent-Informationen.
Auf Solana gibt es drei Arten von Accounts:
- Accounts, die Daten speichern
- Accounts, die ausführbare Programme speichern
- Accounts, die native Programme speichern
Diese Account-Typen lassen sich anhand ihrer Fähigkeiten weiter unterteilen:
- Ausführbare Accounts – Accounts, die Code ausführen können
- Nicht ausführbare Accounts – Accounts zur Datenspeicherung, die keinen Code ausführen können, weil sie keinen Code enthalten
Das Bild oben zeigt einige Beispiele für ausführbare und nicht ausführbare Accounts. Bei den ausführbaren Accounts ist Bubblegum ein Beispiel für einen Programm-Account. Dieses Programm von Metaplex dient zum Erstellen und Verwalten komprimierter NFTs. Das Vote Program ist ein Beispiel für einen nativen Programm-Account. Es erstellt und verwaltet Accounts, die den Abstimmungszustand und die Belohnungen von Validatoren erfassen. Den Unterschied zwischen Programm-Accounts und nativen Programm-Accounts behandeln wir im Abschnitt Was sind Programme?. Zunächst genügt es zu wissen, dass es auf Solana verschiedene Arten ausführbarer Accounts gibt.
Darüber hinaus lässt sich jeder nicht ausführbare Account als Daten-Account einordnen. Beispiele für Daten-Accounts sind:
- Ein zugehöriger Token-Account – ein Account, der Informationen über einen bestimmten Token, dessen Guthaben und dessen Eigentümer enthält, zum Beispiel: Alice besitzt 10 USDC
- Ein System-Account – ein Account, der vom System Program erstellt wurde und ihm gehört
- Ein Stake-Account – ein Account, mit dem Token an Validatoren delegiert werden, um potenziell Belohnungen zu verdienen
Account-Struktur
Accounts sind gemäß dem Struct AccountInfo aufgebaut:
pub struct AccountInfo<'a> {
pub key: &'a Pubkey,
pub lamports: Rc>,
pub data: Rc>,
pub owner: &'a Pubkey,
pub rent_epoch: Epoch,
pub is_signer: bool,
pub is_writable: bool,
pub executable: bool,
}Accounts werden durch ihre Adresse (key) identifiziert, einen eindeutigen öffentlichen 32-Byte-Schlüssel.
Das Feld lamports enthält die Anzahl der Lamports, die diesem Account gehören. Ein Lamport entspricht einem Milliardstel SOL, dem nativen Token von Solana.
data bezeichnet das rohe Daten-Byte-Array, das dieser Account speichert. Es kann alles enthalten, von den Metadaten eines digitalen Assets bis zu Token-Guthaben, und lässt sich durch Programme verändern.
Das Feld owner enthält den Eigentümer dieses Accounts, dargestellt durch die Adresse eines Programm-Accounts. Für den Besitz von Accounts gelten einige Regeln:
- Nur der Eigentümer eines Accounts kann dessen Daten ändern und Lamports abheben
- Jeder kann Lamports auf einen Account einzahlen
- Der Eigentümer eines Accounts kann den Besitz auf einen neuen Eigentümer übertragen, sofern die Daten des Accounts auf null zurückgesetzt werden
Das Feld is_signer ist ein boolescher Wert. Er gibt an, ob der Eigentümer des betreffenden Accounts eine Transaktion signiert hat. Anders gesagt: Das Feld teilt den an der Transaktion beteiligten Programmen mit, ob der Account ein Unterzeichner ist. Ein Unterzeichner besitzt den zum öffentlichen Schlüssel des Accounts gehörenden privaten Schlüssel und ist berechtigt, die vorgeschlagene Transaktion zu genehmigen.
Das Feld is_writable ist ein boolescher Wert, der angibt, ob sich die Daten des Accounts ändern lassen. Solana erlaubt es Transaktionen, Accounts als schreibgeschützt zu kennzeichnen, um parallele Verarbeitung zu ermöglichen. Während die Runtime mehreren Programmen gleichzeitig den Zugriff auf schreibgeschützte Accounts erlaubt, behandelt sie mögliche Schreibkonflikte bei beschreibbaren Accounts anhand einer Verarbeitungsreihenfolge für Transaktionen. Dadurch werden nur konfliktfreie Transaktionen parallel verarbeitet.
Das Feld executable ist ein boolescher Wert, der angibt, ob ein Account Anweisungen verarbeiten kann. Ja, das bedeutet, dass Programme in Accounts gespeichert werden. Darauf gehen wir im nächsten Abschnitt genauer ein. Zunächst müssen wir jedoch das Konzept der Rent erklären.
Das Feld rent_epoch gibt die nächste Epoche an, in der für diesen Account Rent fällig wird. Eine Epoche ist die Anzahl der Slots, für die ein Leader-Zeitplan gilt. Anders als klassische Dateien in einem Betriebssystem haben Accounts auf Solana eine Lebensdauer, die sich in einer Menge von Lamports ausdrückt. Die Idee, dass der Fortbestand eines Accounts von seinem Lamport-Guthaben abhängt, führt uns zum Konzept der Rent.
Rent
Rent sind Speicherkosten, die anfallen, damit Accounts auf Solana bestehen bleiben und im Speicher der Validatoren gehalten werden. Die Rent wird anhand von Epochen berechnet. Eine Epoche ist eine durch Slots definierte Zeiteinheit, in der ein Leader-Zeitplan gilt. So funktioniert die Rent:
- Rent-Erhebung – die Rent wird einmal pro Epoche erhoben. Sie kann auch erhoben werden, wenn eine Transaktion auf einen Account verweist
- Rent-Verteilung – ein Teil der erhobenen Rent wird verbrannt und damit dauerhaft aus dem Umlauf entfernt. Der Rest wird nach jedem Slot an Abstimmungs-Accounts verteilt
- Rent-Zahlung – besitzt ein Account nicht genügend Lamports, um die Rent zu bezahlen, werden seine Daten entfernt und der Account in einem als Garbage Collection bezeichneten Prozess freigegeben
- Rent-Befreiung – Accounts können von der Rent befreit werden, wenn sie ein Mindestguthaben halten, das den Rent-Zahlungen für zwei Jahre entspricht. Alle neuen Accounts müssen diesen von ihrer Größe abhängigen Schwellenwert erfüllen
- Rent-Rückgewinnung – Nutzer können einen Account schließen, um die verbleibenden Lamports zurückzuerhalten. So können sie die in einem Account gespeicherte Rent zurückgewinnen
Die Rent für eine bestimmte Account-Größe lässt sich über den RPC-Endpunkt getMinimumBalanceForRentExemption schätzen. Test Drive vereinfacht dies, indem es die Datenlänge eines Accounts als usize akzeptiert. Auch mit dem CLI-Unterbefehl für Solana-Rent lässt sich die Mindestmenge an SOL schätzen, die ein Account für die Rent-Befreiung benötigt. Zum Zeitpunkt der Erstellung dieses Artikels liefert der Befehl solana rent 20000 beispielsweise Rent-exempt minimum: 0.14009088 SOL zurück.
Adressen auf Solana
Auf Solana gibt es tatsächlich zwei „Arten“ von Adressen. Zur Adresserstellung verwendet Solana ed25519, ein EdDSA-Signaturschema mit SHA-512 (SHA-2) und der elliptischen Kurve Curve22519. Daraus entstehen öffentliche 32-Byte-Schlüssel, die als primäres Adressformat dienen. Da sie nicht gehasht werden, lassen sie sich direkt verwenden.
Damit eine Adresse gültig ist, muss sie einen Punkt auf der ed25519-Kurve darstellen. Allerdings müssen nicht alle Adressen von dieser Kurve abgeleitet sein. Program Derived Addresses (PDAs) werden außerhalb der Kurve erzeugt. Sie besitzen daher keinen zugehörigen privaten Schlüssel und können nicht zum Signieren verwendet werden. PDAs werden über das System Program erstellt und kommen zum Einsatz, wenn Programme Accounts verwalten müssen. Dieser kurze Exkurs soll dich lediglich auf die verschiedenen Adresstypen auf Solana aufmerksam machen. PDAs behandeln wir in einem späteren Artikel.
Wie unterscheiden sich Accounts auf Solana von Accounts auf Ethereum?
Ethereum besitzt zwei primäre Account-Typen: extern verwaltete Accounts (EOAs) und Contract-Accounts. Private Schlüssel kontrollieren EOAs, während Contract-Code die Contract-Accounts steuert. Contract-Accounts können nicht selbstständig Transaktionen initiieren.
Sowohl EOAs als auch Contract-Accounts folgen derselben Account-Struktur:
- Guthaben – jeder Account besitzt ein in Ether gemessenes Guthaben
- Nonce – bei EOAs ist dies die Anzahl der vom Account gesendeten Transaktionen. Bei Contracts ist es die Anzahl der vom Account erstellten Contracts
- Storage Root – ein 256-Bit-Hash des Root-Knotens eines Merkle Patricia Trie, der die Speicherinhalte des Accounts codiert
- CodeHash – der Hash des Codes der Ethereum Virtual Machine (EVM) für den Contract. Er ist unveränderlich. Der Code ändert sich nach der Erstellung also nicht, sein Zustand dagegen schon. Es gibt Ausnahmen für das Aktualisieren von Contracts auf Ethereum, etwa den Einsatz von Proxy-Mustern. Diese liegen jedoch außerhalb des Umfangs dieses Artikels. Bei EOAs ist dies der Hash eines leeren Strings, da EOAs keinen Code enthalten
Solana nutzt ein einheitlicheres Account-Modell, in dem jeder Account potenziell ein Programm sein kann. Die Trennung von Code und Daten schafft eine effizientere und flexiblere Umgebung. Solana-Programme sind zustandslos und interagieren mit verschiedenen Daten-Accounts, ohne dass redundante Deployments erforderlich sind. Dies ist besonders vorteilhaft für DeFi-Anwendungen, bei denen Nutzer mit mehreren Protokollen interagieren möchten, ohne Assets zwischen verschiedenen Programmen zu verschieben. Das Programmiermodell von Ethereum vereint Code und Zustand dagegen in einer einzigen Einheit. Dadurch werden Interaktionen komplexer und aufgrund der Gas-Anforderungen für Zustandsänderungen potenziell teurer.
Solana-Accounts mussten früher Rent bezahlen und ein Mindestguthaben halten, um aktiv zu bleiben. So konnte das Netzwerk ungenutzte oder unzureichend finanzierte Accounts schließlich zurückfordern und das Aufblähen des Zustands reduzieren. Durch neuere Updates gibt es im Mainnet keine Rent zahlenden Accounts mehr – Accounts müssen von der Rent befreit sein. Ethereum verwendet dagegen Gas, um Ressourcen zuzuweisen. In diesem Modell bleibt der Contract-Speicher unbegrenzt bestehen, sofern er nicht ausdrücklich gelöscht wird. Solanas Ansatz bietet eine besser vorhersehbare Kostenstruktur für die Zustandsspeicherung. Die Kosten auf Ethereum können dagegen schwanken und bei hoher Netzwerkauslastung untragbar werden.
Im folgenden Abschnitt untersuchen wir, wie Solana seine Programmlogik vom Zustand trennt. Im Vergleich zum Programmiermodell von Ethereum siehst du, wie dieser modulare Ansatz effizientere On-Chain-Abläufe ermöglicht und Entwicklern zugleich eine transparente, vorhersehbare Kostenstruktur bietet.
Was sind Solana-Programme?
Programme sind ausführbare Accounts, die dem BPF Loader gehören. Die Solana Runtime führt sie aus und verarbeitet dabei Transaktionen und Programmlogik.
Ein besonderes Merkmal des Solana-Programmiermodells ist die Trennung von Code und Daten. Programme sind zustandslos, speichern intern also keinen Zustand. Stattdessen liegen alle für ihre Arbeit erforderlichen Daten in separaten Accounts. Transaktionen übergeben diese Accounts per Referenz an die Programme. Dadurch kann ein einziges, generisches Deployment eines Programms mit verschiedenen Accounts interagieren.
Programme auf Solana können:
- Weitere Accounts besitzen
- Andere Accounts lesen oder ihnen Beträge gutschreiben
- Daten ändern oder eigene Accounts belasten
Es gibt zwei Arten von Programmen:
- On-Chain-Programme – von Nutzern geschriebene Programme, die auf Solana bereitgestellt werden. Ihre Upgrade Authority, üblicherweise der Account, der das Programm bereitgestellt hat, kann sie aktualisieren
- Native Programme – Programme, die in den Kern von Solana integriert sind. Sie stellen die grundlegenden Funktionen bereit, die Validatoren für ihren Betrieb benötigen. Native Programme lassen sich nur durch netzwerkweite Softwareupdates aktualisieren. Gängige Beispiele sind das System Program, das BPF Loader Program und das Vote Program.
Sowohl Nutzer als auch andere Programme können On-Chain- und native Programme aufrufen. Der wesentliche Unterschied liegt in ihren Upgrade-Mechanismen: Die Upgrade Authority kann On-Chain-Programme aktualisieren, native Programme dagegen nur im Rahmen von Cluster-Updates.
Solana Labs pflegt eine ausgewählte Gruppe von On-Chain-Programmen, die als Solana Program Library bekannt ist. Diese Bibliothek ermöglicht verschiedene On-Chain-Operationen, darunter Token-Kredite und die Erstellung von Stake-Pools. Das Associated Token Account Program definiert beispielsweise einen Standard und Mechanismus, um die Wallet eines Nutzers mit seinen jeweiligen Token-Accounts zu verknüpfen. Außerdem entwickelt sich die SPL laufend weiter. Programme wie Token-2022 bauen auf den Funktionen des Token Program auf und erweitern sie.
Programme auf Solana werden üblicherweise in Rust und mithilfe von Anchor entwickelt. Dieses meinungsstarke Framework vereinfacht die Programmerstellung, indem es Boilerplate reduziert und Serialisierung sowie Deserialisierung optimiert. Rust wird bevorzugt, Entwickler sind jedoch nicht darauf beschränkt. Auch C, C++ und jede Sprache, die auf das BPF-Backend von LLVM abzielt, können verwendet werden. Dabei handelt es sich um eine Komponente von LLVM, mit der Programme in BPF-Bytecode kompiliert werden können. Neuere Entwicklungen von Solang und Neon Labs ermöglichen Entwicklern außerdem, Solidity für die Programmentwicklung zu verwenden.
Programme werden üblicherweise auf Localhost und Devnet entwickelt und getestet, bevor sie auf Testnet oder Mainnet Beta bereitgestellt werden. Entwickler können ihr Programm mit dem Befehl solana program deploy <path to program> über die Solana CLI bereitstellen. Nachdem das Programm in ein ELF Shared Object mit dem BPF-Bytecode kompiliert wurde, wird es in den vorgesehenen Solana-Cluster hochgeladen. Bereitgestellte Programme liegen in Accounts, die als executable markiert sind. Die Adresse des Accounts dient dabei als program_id.
Ursprünglich wurden Programme auf Solana mit Accounts bereitgestellt, die doppelt so groß wie das Programm waren. Solanas Update 1.16 unterstützt anpassbare Account-Größen und bietet Entwicklern mehr Flexibilität bei der Ressourcenzuweisung. Entwickler können ihr Programm nun mit einem kleineren Account bereitstellen und diesen später vergrößern.
Wie bereits erwähnt, gelten Programme als zustandslos, da alle Daten, mit denen sie interagieren, in separaten Accounts gespeichert und per Referenz übergeben werden. Jedes Programm besitzt einen einzigen Einstiegspunkt, an dem Anweisungen verarbeitet werden. Dieser nimmt eine program_id, ein Array von Accounts und die Anweisungsdaten als Byte-Array entgegen. Die Solana Runtime führt Programme aus, sobald eine Transaktion sie aufruft.
Was sind Transaktionen?
Transaktionen bilden das Rückgrat der On-Chain-Aktivität. Über sie werden Programme aufgerufen und Zustandsänderungen umgesetzt. Eine Transaktion auf Solana ist ein Bündel von Anweisungen. Es teilt Validatoren mit, welche Aktionen sie für welche Accounts ausführen sollen und ob die erforderlichen Berechtigungen vorliegen.
Eine Transaktion besteht aus drei Hauptteilen:
- Ein Array von Accounts, die gelesen oder beschrieben werden sollen
- Eine oder mehrere Anweisungen
- Eine oder mehrere Signaturen
Transaktionen auf Solana folgen dem Transaction-Struct. Dieses stellt die Informationen bereit, die das Netzwerk zum Verarbeiten und Validieren von Aktionen benötigt. Es ist wie folgt definiert:
pub struct Transaction {
pub signatures: Vec,
pub message: Message,
}Das Feld signatures enthält eine Reihe von Signaturen, die der serialisierten Message entsprechen. Jede Signatur ist einem Account-Schlüssel aus der Liste account_keys der Message zugeordnet, beginnend mit dem Gebührenzahler. Der Gebührenzahler ist der Account, der die bei der Verarbeitung einer Transaktion anfallenden Transaktionsgebühren übernimmt. Üblicherweise ist dies der Account, der die Transaktion initiiert. Die Anzahl der erforderlichen Signaturen entspricht num_required_signatures, das in MessageHeader der Nachricht definiert ist.
Die message selbst ist ein Struct vom Typ Message. Es ist wie folgt definiert:
pub struct Message {
pub header: MessageHeader,
pub account_keys: Vec,
pub recent_blockhash: Hash,
pub instructions: Vec,
}header der Nachricht enthält drei vorzeichenlose 8-Bit-Ganzzahlen: die Anzahl der erforderlichen Signaturen, also num_required_signatures, die Anzahl der schreibgeschützten Unterzeichner und die Anzahl der schreibgeschützten Nicht-Unterzeichner.
Das Feld account_keys listet alle an der Transaktion beteiligten Account-Adressen auf. Accounts mit angefordertem Lese- und Schreibzugriff stehen zuerst, danach folgen schreibgeschützte Accounts.
recent_blockhash ist ein aktueller Blockhash mit einem 32-Byte-SHA-256-Hash. Er ist erforderlich, um anzugeben, wann ein Client das Ledger zuletzt gesehen hat, und definiert die Lebensdauer aktueller Transaktionen. Validatoren lehnen Transaktionen mit einem alten Blockhash ab. Ein aktueller Blockhash hilft außerdem, doppelte Transaktionen zu verhindern, da jede Transaktion abgelehnt wird, die vollständig mit einer früheren übereinstimmt. Muss eine Transaktion aus irgendeinem Grund lange vor ihrer Übermittlung an das Netzwerk signiert werden, kann anstelle eines aktuellen Blockhashs eine dauerhafte Transaktions-Nonce verwendet werden. So bleibt die Transaktion eindeutig.
Das Feld instructions enthält einen oder mehrere CompiledInstruction-Structs, die jeweils eine bestimmte Aktion vorgeben, welche die Validatoren des Netzwerks ausführen sollen.
Anweisungen
Eine Anweisung ist eine Direktive für einen einzelnen Aufruf eines Solana-Programms. Sie ist die kleinste Einheit der Ausführungslogik eines Programms und die grundlegendste operative Einheit auf Solana. Programme interpretieren die von einer Anweisung übergebenen Daten und arbeiten mit den angegebenen Accounts. Der Struct Instruction ist wie folgt definiert:
pub struct Instruction {
pub program_id: Pubkey,
pub accounts: Vec,
pub data: Vec,
}Das Feld program_id gibt den öffentlichen Schlüssel des auszuführenden Programms an. Dies ist die Adresse des Programms, das die Anweisung verarbeitet. Der durch diesen öffentlichen Schlüssel angegebene Eigentümer des Programm-Accounts bestimmt den Loader, der das Programm initialisiert und ausführt. Nach dem Deployment markiert der Loader die On-Chain-Programme im Solana Bytecode Format (SBF) als ausführbar. Die Solana Runtime lehnt alle Transaktionen ab, die versuchen, nicht als ausführbar markierte Accounts aufzurufen.
Das Feld accounts listet die Accounts auf, die die Anweisung lesen oder beschreiben darf. Diese Accounts müssen als AccountMeta-Werte bereitgestellt werden. Jeder Account, dessen Daten die Anweisung verändern könnte, muss als beschreibbar angegeben werden. Andernfalls schlägt die Transaktion fehl. Programme können nicht in Accounts schreiben, die ihnen nicht gehören oder für die ihnen die erforderlichen Berechtigungen fehlen. Dies gilt auch für Änderungen an den Lamports eines Accounts: Werden Lamports von einem Account abgezogen, der dem Programm nicht gehört, schlägt die Transaktion fehl. Lamports zu einem beliebigen Account hinzuzufügen, ist dagegen erlaubt. Das Feld accounts kann außerdem Accounts angeben, die das Programm weder liest noch beschreibt. Dies beeinflusst die Planung der Programmausführung durch die Runtime. Ansonsten werden diese Accounts ignoriert.
data ist ein universeller Vektor vorzeichenloser 8-Bit-Ganzzahlen, der als Eingabe an das Programm übergeben wird. Dieses Feld ist entscheidend, da es die codierten Anweisungen enthält, die das Programm ausführt.
Das Format der Anweisungsdaten ist für Solana unerheblich. Solana unterstützt jedoch nativ die Serialisierung über bincode und borsh (Binary Object Representation Serializer for Hashing). Bei der Serialisierung werden komplexe Datenstrukturen in eine flache Byte-Folge umgewandelt, die übertragen oder gespeichert werden kann. Bei der Wahl der Datencodierung sollte der Aufwand für die Decodierung berücksichtigt werden, da sie vollständig On-Chain erfolgt. Die Borsh-Serialisierung wird häufig gegenüber bincode bevorzugt, da sie eine stabile Spezifikation und eine JavaScript-Implementierung besitzt und im Allgemeinen effizienter ist.
Programme verwenden Hilfsfunktionen, um die Erstellung unterstützter Anweisungen zu vereinfachen. Das System Program stellt beispielsweise eine Hilfsfunktion zum Erstellen der Anweisung SystemInstruction::Assign bereit:
pub fn assign(pubkey: &Pubkey, owner: &Pubkey) -> Instruction {
let account_metas = vec![AccountMeta::new(*pubkey, true)];
Instruction::new(
system_program::id(),
&SystemInstruction::Assign { owner: *owner },
account_metas,
)
}Diese Funktion erstellt eine Anweisung, die bei ihrer Verarbeitung den Eigentümer des angegebenen Accounts in den übergebenen neuen Eigentümer ändert.
Eine einzelne Transaktion kann mehrere Anweisungen enthalten. Sie werden in der aufgeführten Reihenfolge sequenziell und atomar ausgeführt. Entweder sind also alle Anweisungen erfolgreich oder keine. Deshalb kann auch ihre Reihenfolge entscheidend sein. Programme müssen so abgesichert werden, dass sie jede mögliche Anweisungsfolge sicher verarbeiten und potenzielle Exploits verhindern.
Bei der Deinitialisierung könnte ein Programm beispielsweise versuchen, einen Account zu deinitialisieren, indem es dessen Lamport-Guthaben auf null setzt. Dabei geht es davon aus, dass die Solana Runtime den Account löscht. Zwischen Transaktionen ist diese Annahme korrekt, zwischen Anweisungen oder Cross-Program Invocations jedoch nicht. Cross-Program Invocations behandeln wir in einem späteren Artikel. Das Programm sollte die Daten des Accounts ausdrücklich auf null setzen, um sich gegen diese potenzielle Schwachstelle im Deinitialisierungsprozess abzusichern. Andernfalls könnte ein Angreifer eine nachfolgende Anweisung ausgeben und die vermeintliche Löschung ausnutzen, indem er den Account etwa wiederverwendet, bevor die Transaktion abgeschlossen ist.
Was sind versionierte Transaktionen?
Transaktionen auf Solana verwenden die Standards für die Maximum Transmission Unit (MTU) von IPv6, um Daten in einem Cluster schnell und zuverlässig zu übertragen. Solanas Netzwerk-Stack nutzt eine konservative MTU-Größe von 1280 Byte. Nach Abzug des Platzes für Header bleiben 1232 Byte für Paketdaten. Solana-Transaktionen sind daher auf diese Größe beschränkt.
Diese Größenbeschränkung ermöglicht verschiedene Netzwerkoptimierungen, begrenzt aber zugleich die Komplexität der Operationen, die eine einzelne Transaktion ausführen kann. Da jede Account-Adresse 32 Byte Speicher belegt, kann eine Transaktion ohne Anweisungen bis zu 35 Accounts speichern. Diese Einschränkung erschwert Anwendungsfälle, die mehr als 35 signaturfreie Accounts in einer einzigen Transaktion benötigen.
Um dieses Problem zu lösen, wurde ein neues Transaktionsformat eingeführt, das mehrere Versionen von Transaktionsformaten unterstützt. Die Solana Runtime unterstützt derzeit zwei Transaktionsversionen:
legacy– das ursprüngliche Transaktionsformat0(Version 0) – das neueste Transaktionsformat mit Unterstützung für Address Lookup Tables
Version 0 wurde veröffentlicht, um Address Lookup Tables (ALTs) zu unterstützen. Im Wesentlichen speichern sie Account-Adressen On-Chain in einer tabellenähnlichen Datenstruktur. Diese Tabellen sind separate Accounts, die Account-Adressen speichern. Eine Transaktion kann über einen 1 Byte großen u8-Index auf sie verweisen. Dadurch sinkt die Größe einer Transaktion deutlich, da jeder enthaltene Account statt 32 Byte nur noch 1 Byte benötigt. ALTs sind besonders nützlich für komplexe Operationen mit vielen Accounts, wie sie häufig in DeFi-Anwendungen vorkommen.
Dieses Diagramm basiert auf dem Abschnitt zu versionierten Transaktionen im Solana Cookbook
Der Begriff „versionierte Transaktionen“ bezeichnet die Art, wie Solana sowohl das Legacy- als auch das Version-0-Transaktionsformat unterstützt. Dieser Ansatz gewährleistet Komponierbarkeit und nutzt zugleich Verbesserungen der Runtime.
Struktur einer versionierten Transaktion
Eine VersionedTransaction ist wie folgt definiert:
pub struct VersionedTransaction {
pub signatures: Vec,
pub message: VersionedMessage,
}Das Feld signatures ist eine Liste der Signaturen der Transaktionsunterzeichner. Sie authentifizieren die Transaktion und wahren ihre Integrität. message ist der eigentliche Inhalt der Transaktion. Dieser wird vom Typ VersionedMessage gekapselt, einem schlanken Enum-Wrapper, der sowohl Legacy- als auch Version-0-Nachrichten verarbeitet:
pub enum VersionedMessage {
Legacy(Message),
V0(Message),
}Die Nachrichtenversion wird beim Serialisieren durch das erste Bit bestimmt. Ist das erste Bit gesetzt, bestimmen die verbleibenden 7 Bit, welche Version von Message ab Version 0 serialisiert wird. Ist das erste Bit nicht gesetzt, werden alle Bytes zur Codierung des Legacy-Formats Message verwendet. Der Grund dafür ist, dass es zwei gleichnamige Message-Structs gibt, die jedoch in unterschiedliche Module aufgeteilt sind: legacy und v0.
Eine Message stellt das komprimierte interne Format einer Transaktion dar. Es dient der Netzwerkübertragung und der Verarbeitung durch die Runtime. Es umfasst eine lineare Liste aller von den Transaktionsanweisungen verwendeten Accounts, eine MessageHeader mit Angaben zur Struktur des Account-Arrays, einen aktuellen Blockhash und eine kompakte Codierung der Anweisungen der Nachricht. Dies ist die Struktur des v0-Structs Message:
pub struct Message {
pub header: MessageHeader,
pub account_keys: Vec,
pub recent_blockhash: Hash,
pub instructions: Vec,
pub address_table_lookups: Vec,
}Der Unterschied zwischen einer Legacy-Nachricht und einer v0-Nachricht besteht im Feld address_table_lookups.
Integration des Programmiermodells in den Transaktionsfluss von Solana
Das Programmiermodell von Solana ist eng mit seinen Account- und Transaktionssystemen verknüpft. So greifen die Konzepte ineinander:
- Accounts als Zustand – Accounts auf Solana dienen Programmen als Zustandscontainer. Das Programmiermodell dreht sich darum, die in diesen Containern gespeicherten Daten als Reaktion auf Anweisungen zu ändern
- Anweisungen – Programme definieren die Logik zur Verarbeitung der in einer Transaktion enthaltenen Anweisungen. Diese Anweisungen sind die ausführbaren Komponenten, die mit Account-Daten interagieren
- Serialisierung und Verarbeitung – beim Serialisieren einer Transaktion bestimmen die Programmanweisungen die Änderungen an den Account-Zuständen. Der Serialisierungsprozess berücksichtigt das Design des Programms, unabhängig davon, ob es das Legacy- oder das Version-0-Transaktionsformat verwendet
- Atomarität – das Programmiermodell von Solana sorgt für eine atomare Verarbeitung von Anweisungen. Programme müssen gleichzeitige Transaktionen sicher und effizient verarbeiten können
- Skalierbarkeit – das Programmiermodell von Solana unterstützt Skalierbarkeit durch Funktionen wie Address Lookup Tables (ALTs). Diese Tabellen verkleinern eine Transaktion und erhöhen die Anzahl der Accounts, auf die sie verweisen kann
Beim Programmiermodell von Solana geht es nicht nur darum, Code zu schreiben. Du musst auch verstehen, wie dieser Code im größeren Ökosystem interagiert. Accounts sind für dieses Modell unverzichtbar, da sie die primäre Möglichkeit darstellen, Daten im Netzwerk zu speichern und zu verändern. Transaktionen ermöglichen On-Chain-Aktivität, indem sie Validatoren mitteilen, welche Daten erstellt, aktualisiert oder gelöscht werden müssen. Entwickler müssen diese Aspekte genau verstehen, um Anwendungen zu entwickeln, die für Performance und das Zusammenspiel im Solana-Ökosystem optimiert sind.
Fazit
Glückwunsch! In diesem Artikel haben wir uns durch die komplexe Systemarchitektur von Solana gearbeitet und Cluster als monolithische Daten-Heaps betrachtet. Wir haben gesehen, wie dieser Heap in getrennte Speicherbereiche unterteilt ist, die als Accounts bezeichnet werden und das Rückgrat des Solana-Programmiermodells bilden. Accounts speichern alles, von den Token der Nutzer bis zu den Programmen, die das Verhalten des Netzwerks bestimmen. Transaktionen verändern all diese Inhalte.
Für Entwickler ist es entscheidend, Solanas Ansatz für dezentrales Computing zu verstehen. Wer Anwendungen entwickeln will, die Solanas Möglichkeiten voll ausschöpfen, muss die Feinheiten von Accounts, Programmen und Transaktionen kennen. Es geht darum, ein System zu verstehen, in dem Code vom Zustand entkoppelt ist. Daraus entstehen zustandslose Programme, die über Accounts mit Daten interagieren und ein beispielloses Maß an Komponierbarkeit und Aktualisierbarkeit bieten.
Auch für Investoren und gelegentliche Nutzer ist es wichtig zu verstehen, wie Solanas Design ein robustes, flexibles und effizientes Ökosystem schafft. Nur so lässt sich beurteilen, wie tragfähig die Plattform ist und wie gut sie innovative Anwendungen fördern kann, die ausschließlich auf Solana möglich sind.
Wenn du bis hierher gelesen hast, anon: Vielen Dank! Bereit, tiefer einzusteigen? Tritt unserem Discord bei und beginne noch heute mit der Programmierung auf Solana.
Zusätzliche Ressourcen und weiterführende Literatur
Ähnliche Artikel
Helius abonnieren
Bleib bei der Solana-Entwicklung auf dem Laufenden und erhalte Updates, wenn wir neue Beiträge veröffentlichen


