Why am I sharing my travel stories?
Founder & CEO of TruStory. I have a passion for understanding things at a fundamental level and sharing it as clearly as possible.
Special thanks to inlak16 for translating this post into German
Wahrscheinlich hast du von der Ethereum-Blockchain schon gehört, egal, ob du weißt, was das ist oder nicht. Es war in letzter Zeit oft in den Nachrichten, einschließlich der Cover einiger großer Zeitschriften, aber die Lektüre dieser Artikel kann wie Kauderwelsch sein, wenn man keine Grundlage für das hat, was genau Ethereum ist. Also was ist Ethereum? Im Wesentlichen ist es eine öffentliche Datenbank, die eine permanente Aufzeichnung digitaler Transaktionen führt. Wichtig ist, dass diese Datenbank keine zentrale Autorität benötigt, um sie zu pflegen und zu sichern. Stattdessen fungiert sie als „vertrauensloses“ Transaktionssystem – ein Rahmenwerk, in dem Einzelpersonen Peer-to-Peer-Transaktionen tätigen können, ohne einem Dritten ODER einander vertrauen zu müssen. Bist du immer noch verwirrt? Hier kommt dieser Beitrag ins Spiel. Mein Ziel ist es, zu erklären, wie Ethereum auf technischer Ebene funktioniert, ohne komplexe Mathematik oder beängstigende Formeln. Auch wenn du kein Programmierer bist, hoffe ich, dass du zumindest mit einem besseren Verständnis der Technik diesen Text beendest. Wenn einige Teile zu technisch und schwer zu verstehen sind, ist das vollkommen in Ordnung! Es ist wirklich nicht nötig, jedes kleine Detail zu verstehen. Ich empfehle, sich einfach darauf zu konzentrieren, die Dinge auf breiter Ebene zu verstehen.
Viele der in diesem Beitrag behandelten Themen sind eine Aufschlüsselung der Konzepte aus dem Yellow Paper. Ich habe meine eigenen Erklärungen und Diagramme hinzugefügt, um das Verständnis von Ethereum zu erleichtern. Wer den Mut hat, die technische Herausforderung anzunehmen, kann auch das Ethereum Yellow Paper lesen. Los gehts!
Eine Blockchain ist eine „kryptographisch gesicherte transaktionale Singleton-Maschine mit Shared State.“ [1] Ziemlich viel auf einmal, oder? Lass uns das vereinfachen.
Ethereum setzt dieses Blockchain-Paradigma um.
Die Ethereum-Blockchain ist im Wesentlichen eine transaktionsbasierte Zustandsmaschine (State Machine). In der Informatik bezieht sich eine Zustandsmaschine auf etwas, das eine Reihe von Eingaben lesen wird, und basierend auf diesen Eingaben daraufhin in einen neuen Zustand übergehen wird.
Mit Ethereums Zustandmaschine beginnen wir mit einem "Genesis-Zustand." Dies ist gleichzusetzen mit einem unbeschriebenen Blatt, bevor irgendwelche Transaktionen in dem Netzwerk stattgefunden haben. Wenn Transaktionen ausgeführt werden, wird dieser Genesis-Zustand in einen finalisierten Zustand überführt. Zu jedem Zeitpunkt repräsentiert dieser finalisierte Zustand den aktuellen Zustand von Ethereum.
Der Zustand von Ethereum hat Millionen von Transaktionen. Diese Transaktionen werden in „Blöcke“ gruppiert. Ein Block enthält eine Reihe von Transaktionen und jeder Block wird mit seinem vorherigen Block verkettet.
Um einen Übergang von einem Zustand zum nächsten zu verursachen, muss eine Transaktion gültig sein. Damit eine Transaktion als gültig angesehen werden kann, muss sie einen Validierungsprozess durchlaufen, der als Mining bekannt ist. Mining ist ein Prozess, in dem eine Gruppe von Nodes (d.h. Computer) ihre Rechenressourcen nutzen, um einen Block gültiger Transaktionen zu erstellen.
Jede Node im Netzwerk, die sich als Miner erklärt, kann versuchen, einen Block zu erstellen und zu validieren. Viele Miner aus der ganzen Welt versuchen, Blöcke gleichzeitig zu erstellen und zu validieren. Jeder Miner stellt einen mathematischen „Beweis“ zur Verfügung, wenn er einen Block an die Blockchain sendet. Wenn dieser Beweis existiert, muss der Block gültig sein.
Damit ein Block zur Haupt-Blockchain hinzugefügt werden kann, muss der Miner ihn schneller nachweisen als jeder andere im Wettbewerb stehende Miner. Der Prozess der Validierung jedes Blocks durch einen Miner stellt einen mathematischen Beweis dar, was auch als „Proof of Work“ bekannt ist.
Ein Miner, der einen neuen Block validiert, wird für diese Arbeit mit einem bestimmten Wert belohnt. Was ist dieser Wert? Die Ethereum-Blockchain verwendet einen systemeigenen digitalen Token namens „Ether“. Jedes Mal, wenn ein Miner einen Block beweist, werden neue Ether-Token generiert und vergeben.
Du fragst dich vielleicht: Was garantiert, dass sich alle an einer Kette von Blöcken halten? Wie können wir sicher sein, dass es keine Ansammlung von Minern gibt, die sich entscheiden, ihre eigene Blockkette zu erstellen?
Zuvor haben wir eine Blockchain als transaktionale Singleton-Maschine mit Shared State definiert. Mit dieser Definition können wir verstehen, dass der richtige aktuelle Zustand eine einzige globale Wahrheit ist, die jeder akzeptieren muss. Mehrere Zustände (oder Ketten) würden das gesamte System ruinieren, da es unmöglich wäre, sich darauf zu einigen, welcher Zustand der richtige ist. Würden die Ketten voneinander abweichen, könntest du 10 Coins auf einer Kette besitzen, 20 auf einer anderen und 40 wiederum auf noch einer anderen. In diesem Szenario gäbe es keine Möglichkeit, zu ermitteln, welche Kette die „gültigste“ ist.
Wann immer mehrere Pfade generiert werden, kommt es zu einer „Abspaltung (Fork)“. Wir wollen Forks normalerweise vermeiden, weil sie das System stören und die Menschen zwingen, die Blockchain zu wählen, an die sie „glauben“.
Um festzustellen, welcher Pfad am ehesten gültig ist und um mehrere Ketten zu verhindern, verwendet Ethereum einen Mechanismus, der “GHOST Protokoll” genannt wird.
“GHOST” = “Greedy Heaviest Observed Subtree”
Einfach ausgedrückt: Das GHOST-Protokoll besagt, dass wir den Pfad wählen müssen, auf dem am meisten Berechnungen durchgeführt wurden. Eine Möglichkeit, diesen Pfad zu bestimmen, ist die Blocknummer des neuesten Blocks (des „Leaf-Blocks“) zu verwenden, der die Gesamtzahl der Blöcke im aktuellen Pfad repräsentiert (ohne den Genesis-Block zu zählen). Je höher die Blocknummer, desto länger der Weg und desto größer der Mining-Aufwand zum neuesten Block auf dem Pfad. Mit dieser Argumentation können wir uns auf die anerkannte Version des aktuellen Zustands einigen.
Da du jetzt eine grobe Übersicht über eine Blockchain erhalten hast, lass uns tiefer in die Hauptkomponenten, aus denen das Ethereum-System besteht, eintauchen:
Eine Bemerkung bevor ich anfange: Wann immer ich „Hash“ von X sage, beziehe ich mich auf den KECCAK-256 Hash, den Ethereum benutzt.
Der globale „Shared State“ von Ethereum besteht aus vielen kleinen Objekten („Konten“), die in der Lage sind, durch ein nachrichtenübergreifendes Framework miteinander zu interagieren. Jedem Konto ist ein Zustand und eine 20-Byte-Adressezugeordnet. Eine Adresse in Ethereum ist ein 160-Bit-Identifikator, der verwendet wird, um jedes Konto zu identifizieren. Es gibt zwei Arten von Konten:
Es ist wichtig, einen grundlegenden Unterschied zwischen externen Konten und Vertragskonten zu verstehen. Ein externes Konto kann Nachrichten an andere externe Konten ODER an andere Vertragskonten verschicken, indem eine Transaktion mit ihrem privaten Schlüssel erstellt und signiert wird. Eine Transaktion zwischen zwei externen Konten ist einfach ein Werttransfer. Aber eine Nachricht von einem externen Konto an ein Vertragskonto aktiviert den Code des Vertragskontos und erlaubt ihm verschiedene Aktionen (z.B. einen Tokentransfer, das Schreiben in den internen Speicher, neue Token zu generieren, Berechnungen durchzuführen, neue Verträge zu erstellen etc.).
Im Gegensatz zu externen Konten können Vertragskonten keine neuen Transaktionen alleine starten. Stattdessen können Vertragskonten nur Transaktionen als Reaktion auf andere Transaktionen (von einem externen Konto oder Vertragskonto), die sie erhalten haben, abschicken. Wir werden mehr über Anrufe von Vertrag zu Vertrag im Abschnitt “Transaktionen und Nachrichten” erfahren.
Daher wird jede Aktion, die auf der Ethereum-Blockchain auftritt, immer durch Transaktionen in Gang gesetzt, die von extern kontrollierten Konten gesendet werden.
Der Kontostand besteht aus vier Komponenten, die unabhängig vom Kontotyp vorhanden sind:
Okay, wir wissen, dass der globale Status von Ethereum aus einer Zuordnung zwischen Kontoadressen und Kontozuständen besteht. Diese Zuordnung wird in einer Datenstruktur gespeichert, die als Merkle-Patricia-Baum bekannt ist. Ein Merkle-Baum (oder auch „Merkle trie“ genannt) ist eine Art Binärbaum bestehend aus einer Reihe von Nodes mit:
Die Daten am unteren Rand des Baumes werden generiert, indem die Daten, die wir speichern möchten, in Chunks aufgeteilt werden. Diese Chunks werden dann in Buckets geteilt und deren Hashes werden immer wieder verrechnet, bis die Gesamtzahl der verbleibenden Hashes 1 ergibt: Den Root-Hash.
Dieser Baum muss einen Schlüssel für jeden in ihm gespeicherten Wert haben. Beginnend mit der Root Node des Baumes sollte dieser Schlüssel dir sagen, welchem Unterknoten du folgen musst, um den entsprechenden Wert zu erhalten, der in der Leaf Node gespeichert wird. Im Fall von Ethereum liegt die Schlüssel-/Wert-Zuordnung für den Baum des Status zwischen Adressen und ihren zugehörigen Konten, einschließlich des Kontostands, der Nonce, des CodeHash und des StorageRoots für jedes Konto (wobei die StorageRoot selbst ein Baum ist).
Quelle: Ethereum Whitepaper Die gleiche Trie-Struktur wird auch verwendet, um Transaktionen und Belege zu speichern. Genauer gesagt hat jeder Block einen „Header“, der den Hash der Root Node von drei verschiedenen Merkel-Trie-Strukturen speichert, einschließlich:
Die Fähigkeit, all diese Informationen effizient in Merkle-Tries zu speichern, ist in Ethereum äußerst nützlich für das, was wir „Light Clients“ oder „Light Nodes“ nennen. Denke daran, dass eine Blockchain von einer Reihe von Nodes am Laufen gehalten wird. Im Großen und Ganzen gibt es zwei Arten von Nodes: Full Nodes und Light Nodes.
Eine vollständige Archiv-Node synchronisiert die Blockchain durch Herunterladen der gesamten Kette, vom Genesis-Block bis zum aktuellen Head-Block, in denen alle enthaltenen Transaktionen ausgeführt werden, um zum aktuellen Zustand zu gelangen. In der Regel speichern Miner die vollständige Archiv-Node, da sie für den Miningprozess dazu verpflichtet sind. Es ist auch möglich, eine vollständige Node ohne jede Transaktion herunterzuladen. Unabhängig davon enthält jede Full Node die gesamte Kette. Aber neben dem Fall, dass eine Node jede Transaktion ausführen oder historische Daten abfragen muss, gibt es wirklich keinen Grund die gesamte Kette zu speichern. Hier kommt das Konzept der Light Node ins Spiel. Anstatt die volle Kette herunterzuladen und zu speichern und alle Transaktionen auszuführen, laden Light Nodes nur die Kette der Block-Kopfzeilen vom Genesis-Block zum aktuellen Head-Block herunter, ohne Transaktionen auszuführen oder einen zugehörigen Zustand zu erhalten. Weil Light Nodes Zugriff auf Block-Kopfzeilen haben, die Hashes von drei Tries enthalten, können sie immer noch leicht nachprüfbare Antworten für Transaktionen, Ereignisse, Salden, etc. generieren und erhalten. Der Grund das dies funktioniert ist, dass sich Hashes im Merkle-Baum nach oben weiter ausbreiten – Wenn ein böswilliger Benutzer versucht, eine gefälschte Transaktion in den unteren Teil eines Merkle-Baums auszutauschen, führt dies zu einer Änderung im Hash des Knotens darüber, was wiederum den Hash des Knotens darüber verändert, und so weiter, bis es schließlich auch die Wurzel des Baumes ändert.
Jede Node, die ein Stück Daten verifizieren möchte, kann dazu einen so genannten „Merkle-Beweis“ verwenden. Ein Merkle-Beweis besteht aus:
Jeder, der den Beweis liest, kann überprüfen, ob das Hashing für diesen Zweig den ganzen Baum hinauf konsistent ist. Deshalb kann bewiesen werden, dass der gegebene Datenteil tatsächlich an dieser Position im Baum liegt.
Zusammenfassend ist der große Vorteil der Verwendung eines Merkle-Patricia-Baumes, dass die Root Node dieser Struktur kryptografisch von den im Baum gespeicherten Daten abhängt. So kann der Hash der Root Node als sichere Identität für diese Daten verwendet werden. Da der Block-Header den Root-Hash der Zustands-, Transaktions- und Beleg-Bäume enthält, kann jede Node einen kleinen Teil des Zustands von Ethereum validieren, ohne den gesamten Zustand speichern zu müssen, der in der Größe potenziell unbegrenzt sein kann.
Ein sehr wichtiges Konzept in Ethereum ist das Gebührenkonzept. Jede Berechnung, die infolge einer Transaktion im Ethereum-Netzwerk auftritt, verursacht eine Gebühr – Nichts ist umsonst! Diese Gebühr wird in sogenanntem „Gas“ bezahlt.
Gas ist die Einheit, mit der die für eine bestimmte Berechnung erforderlichen Gebühren gemessen werden. Der Gaspreis ist der Betrag von Ether den du bereit bist, für jede Gaseinheit auszugeben und wird in „gwei“ gemessen. „Wei“ ist die kleinste Einheit des Ether, wobei 1.000.000.000.000.000.000 (10¹⁸) Wei = 1 Ether repräsentiert. Ein gwei ist 1.000.000.000 Wei.
Bei jeder Transaktion legt ein Absender ein Gas-Limit und einen Gaspreis fest. Das Produkt von Gaspreis und Gas-Limit entspricht der maximalen Menge an Wei, die der Absender für die Durchführung einer Transaktion zu zahlen bereit ist.
Beispielsweise setzt der Absender das Gas-Limit auf 50.000 und einen Gaspreis auf 20 Gwei. Dies bedeutet, dass der Absender bereit ist, maximal 50.000 x 20 gwei = 1.000.000.000.000.000 Wei = 0,001 Ether auszugeben, um diese Transaktion auszuführen.
Denke daran, dass das Gas-Limit das maximale Gas darstellt, für das der Absender bereit ist, Geld auszugeben. Wenn er genug Ether in seinem versendendem Account hat, um dieses Maximum zu decken, sollte die Transaktion grundsätzlich funktionieren. Dem Absender wird jedes ungenutzte Gas am Ende der Transaktion zum ursprünglichen Preis zurückerstattet.
Falls der Absender nicht das notwendige Gas zur Verfügung stellt, um die Transaktion auszuführen, ist für die Transaktion „kein Gas mehr vorhanden“ und wird als ungültig angesehen. In diesem Fall wird die Transaktionsverarbeitung abgebrochen und alle eingetretenen Zustandsänderungen rückgängig gemacht, so dass wir wieder in den Zustand von Ethereum vor der Transaktion zurückkehren. Zusätzlich wird eine Aufzeichnung des Fehlschlags der Transaktion aufgezeichnet, die beinhaltet, welche Transaktion versucht wurde und wo sie fehlgeschlagen ist. Und da die Maschine bereits Arbeit aufgewendet hat, um die Berechnungen laufen zu lassen, bevor sie mit der Rückmeldung "kein Gas mehr vorhanden" das gesamte Gas verbraucht hat, wird logischerweise kein Gas an den Absender zurückerstattet.
Wohin fließt dieses Gas-Geld eigentlich? Das gesamte Geld, das der Absender für Gas ausgibt, wird an den „Begünstigten“ geschickt, was üblicherweise die Adresse des Miners ist. Da Miner die Arbeit aufwenden, Berechnungen durchzuführen und Transaktionen zu validieren, erhalten Miner die Gasgebühr als Belohnung.
Je höher der Gaspreis, den der Absender zu zahlen bereit ist, desto höher ist der Wert, den der Miner aus der Transaktion zieht. Es wird also wahrscheinlicher, dass die Miner die Transaktion übernehmen. Auf diese Weise können Miner frei wählen, welche Transaktionen sie validieren oder ignorieren möchten. Um Absender auf den benötigten Gaspreis hinzuweisen, haben Miner die Möglichkeit, den Mindestpreis für Gas zu bewerben, zu dem sie Transaktionen durchführen werden.
Gas wird nicht nur genutzt, um Berechnungsschritte zu bezahlen, sondern auch zur Bezahlung der Speicherauslastung verwendet. Die Gesamtgebühr für die Speicherung ist proportional zum kleinsten Vielfachen von 32 Bytes der verwendeten Datenmenge. Gebühren für die Speicherung haben einige nuancierte Aspekte. Da zum Beispiel die Vergrößerung des Speichers die Größe der Ethereum Zustand-Datenbank auf allen Nodes erhöht, gibt es den Anreiz, die Menge der gespeicherten Daten gering zu halten. Aus diesem Grund wird die Gebühr für die Ausführung einer Operation aufgehoben UND eine Rückerstattung für die Freigabe von Speicherplatz gegeben, wenn eine Transaktion einen Schritt macht, der einen Eintrag im Speicher löscht.
Ein wichtiger Aspekt der Arbeitsweise von Ethereum ist, dass jede einzelne Operation, die vom Netzwerk ausgeführt wird, gleichzeitig von jeder Full Node ausgeführt wird. Allerdings sind rechnerische Schritte auf der Ethereum Virtual Machine sehr kostspielig. Daher sind Smart Contracts von Ethereum am besten für einfache Aufgaben wie das Ausführen einer einfachen Geschäftslogik oder die Überprüfung von Signaturen und anderen kryptographischen Objekten geeignet, anstatt komplexere Anwendungen, wie zum Beispiel Dateispeicherung, E-Mail oder maschinelles Lernen auszuführen, die unnötig das Netzwerk belasten würden. Die Erhebung von Gebühren verhindert, dass Nutzer das Netzwerk überlasten. Ethereum ist eine vollständige Turing-Sprache. (Kurz gesagt, eine Turing-Maschine ist eine Maschine, die jeden Computer-Algorithmus simulieren kann (für diejenigen, die nicht mit Turing-Maschinen vertraut sind, schau dir dies und dies an). Dies erlaubt Programmierschleifen und macht Ethereum anfällig für das Halting Problem, ein Problem, bei dem du nicht feststellen kannst, ob ein Programm unendlich laufen wird oder nicht. Wenn es keine Gebühren gäbe, könnte ein böswilliger Akteur leicht versuchen, das Netzwerk zu stören, indem er eine unendliche Schleife innerhalb einer Transaktion ausführt, ohne Rückwirkungen für sich selbst befürchten zu müssen. So schützen Gebühren das Netzwerk vor vorsätzlichen Angriffen. Vielleicht denkst du: „Warum müssen wir auch für die Speicherung bezahlen?" Nun, genau wie die Berechnung ist auch die Speicherung im Ethereum Netzwerk ein Kostenfaktor, den das gesamte Netzwerk zu tragen hat.
Wir haben vorhin angemerkt, dass Ethereum eine transaktionsbasierte Zustandsmaschine ist. Mit anderen Worten: Transaktionen zwischen verschiedenen Konten sind das, was den globalen Zustand von Ethereum von einem Zustand zum nächsten verschiebt.
Im grundlegendsten Sinne ist eine Transaktion eine kryptographisch signierte Ausführung, die von einem externen Konto generiert, serialisiert und dann an die Blockchain übermittelt wird.
Es gibt zwei Arten von Transaktionen: Message Calls (Nachrichtenanrufe) und Contract Creations (d.h. Transaktionen, die neue Ethereum-Verträge erstellen).
Alle Transaktionen enthalten die folgenden Komponenten, unabhängig von ihrer Art:
Wir haben im Abschnitt „Konten“ gelernt, dass sowohl Nachrichtenanrufe als auch vertragsserstellende Transaktionen immer von externen Konten initiiert und an die Blockchain übermittelt werden. Du kannst es dir so vorstellen, dass Transaktionen die Brücke zwischen der Außenwelt und dem inneren Zustand von Ethereum bilden.
Aber das heißt nicht, dass Verträge nicht mit anderen Verträgen sprechen können. Verträge, die im globalen Rahmen von Ethereums Zustand existieren, können mit anderen Verträgen innerhalb desselben Rahmens sprechen. Sie kommunizieren über „Nachrichten“ oder „interne Transaktionen“ mit anderen Verträgen. Wir können Nachrichten oder interne Transaktionen mit normalen Transaktionen vergleichen, mit dem großen Unterschied, dass sie NICHT von externen Konten generiert werden. Stattdessen werden sie durch Verträge erzeugt. Sie sind virtuelle Objekte, die im Gegensatz zu Transaktionen nicht serialisiert sind und nur in der Ethereum-Ausführungsumgebung existieren.
Wenn ein Vertrag eine interne Transaktion an einen anderen Vertrag sendet, wird der zugehörige Code ausgeführt, der auf dem Empfängervertragskonto existiert.
Eine wichtige Sache zu beachten ist, dass interne Transaktionen oder Nachrichten kein gasLimit. enthalten, da das Gas-Limit durch den externen Ersteller der ursprünglichen Transaktion (d.h. ein externes Konto) bestimmt wird. Das vom externen Account gesetzte Gas-Limit, muss hoch genug sein, um die Transaktion samt aller Unterausführungen, die als Folge dieser Transaktion auftreten, wie zum Beispiel Nachrichten von Vertrag zu Vertrag, durchzuführen. Wenn in der Kette von Transaktionen und Nachrichten einer bestimmte Ausführung von Nachrichten das Gas aufgebraucht ist, wird die Ausführung dieser Nachricht zusammen mit allen nachfolgend durch die Ausführung ausgelösten Meldungen rückgängig gemacht. Die übergeordnete Ausführung muss jedoch nicht rückgängig gemacht werden.
Alle Transaktionen sind in „Blöcken“ zusammengefasst. Eine Blockchain enthält eine Reihe von Blöcken, die miteinander verkettet sind. Im Etherum besteht ein Block aus:
Was ist denn jetzt ein „Verwandter?“ Ein Verwandter (ommer/uncle) ist ein Block, dessen Eltern-Block dem Großeltern-Block des aktuellen Blocks entspricht. Lass uns nun herausfinden, wofür Verwandte verwendet werden und warum ein Block die Block-Headers für Verwandte enthält.
Aufgrund der Art und Weise, wie Ethereum aufgebaut ist, sind Blockzeiten viel niedriger (~15 Sekunden), als die anderer Blockchains, wie Bitcoin (~10 Minuten). Dies ermöglicht eine schnellere Transaktionsabwicklung. Eine der Nachteile der kürzeren Blockzeiten besteht jedoch darin, dass die Miner wettbewerbsfähigere Blocklösungen finden. Diese konkurrierenden Blöcke werden auch als „verwaiste Blöcke“ bezeichnet (d.h. abgebaute Blöcke, die nicht in die Hauptkette aufgenommen werden). Das Ziel der Verwandten ist es, die Miner dafür zu belohnen, dass sie diese verwaisten Blöcke aufgenommen haben. Die Verwandten die von Minern angenommen werden, müssen „gültig“ sein. Das heisst, sie müssen innerhalb der sechsten Generation oder kleiner des aktuellen Blocks entstanden sein. Nach sechs Kindern können verwaiste Blöcke nicht mehr referenziert werden (weil das Aufnehmen älterer Transaktionen die Dinge etwas verkomplizieren würde). Verwandten-Blöcke erhalten eine kleinere Belohnung als ein vollständiger Block. Trotzdem gibt es noch genügend Anreiz für Miner, diese verwaisten Blöcke einzubeziehen und eine Belohnung zu erhalten.
Lass uns für einen Moment auf die Blöcke zurückkommen. Wir haben bereits erwähnt, dass jeder Block einen Block-„Header“ hat, aber was genau ist das?
Ein Block-Header ist ein Teil des Blocks, bestehend aus:
Beachte, dass jeder Block-Header drei Trie-Strukturen enthält:
Diese Trie-Strukturen sind nichts anderes als die Merkl- Patricia-Bäume, über die wir zuvor gesprochen haben. Darüber hinaus gibt es einige Begriffe aus der obigen Beschreibung, die es wert sind, erklärt zu werden. Lass uns einen Blick darauf werfen.
Ethereum ermöglicht es mit Hilfe von Logs, verschiedene Transaktionen und Nachrichten zu verfolgen. Ein Vertrag kann explizit ein Log erzeugen, indem er „Ereignisse“ (Events) definiert, die er protokollieren will. Ein Logeintrag enthält:
Logs werden in einem Bloomfilter gespeichert, der die unzähligen Logdaten effizient speichert.
Die im Header gespeicherten Logs stammen aus den Log-Informationen, die im Transaktionsbeleg enthalten sind. Genau wie Sie eine Quittung erhalten, wenn Sie etwas in einem Geschäft kaufen, erzeugt Ethereum einen Beleg für jede Transaktion. Wie du dir vorstellen kannst, enthält jeder Beleg bestimmte Informationen über die Transaktion. Dieser Beleg enthält Infos wie:
Die „Schwierigkeit“ (difficulty) eines Blocks wird verwendet, um Konsistenz in der Zeit der Validierung von Blöcken durchzusetzen. Der Genesis-Block hat eine Schwierigkeit von 131.072. Mit einer speziellen Formel wird die Schwierigkeit jedes nachfolgenden Blocks berechnet. Wird ein bestimmter Block schneller validiert als der vorherige Block, erhöht das Ethereum-Protokoll die Block Difficulty. Die Schwierigkeit des Blocks beeinflusst die Nonce,, die ein Hash ist, der beim Minen eines Blocks berechnet werden muss, wenn der Proof-of-Work-Algorithmus verwendet wird. Das Verhältnis zwischen Difficulty und Nonce ist mathematisch formalisiert als:
Hier stellt Hd die Schwierigkeit dar. Die einzige Möglichkeit, eine Nonce zu finden, die eine Schwierigkeitsschwelle erfüllt, besteht darin, alle Möglichkeiten mit dem Proof-of-Work-Algorithmus aufzuzählen. Die erwartete Zeit, um eine Lösung zu finden, ist proportional zum Schwierigkeitsgrad - je höher der Schwierigkeitsgrad, desto schwieriger wird es, die Nonce zu finden, und desto schwieriger ist es, den Block zu validieren, was wiederum die Zeit erhöht, die für die Validierung eines neuen Blocks benötigt wird. Also kann das Protokoll durch Anpassung der Schwierigkeit eines Blocks einstellen, wie lange es dauert, um einen Block zu validieren. Wenn andererseits die Validierungszeit langsamer wird, verringert das Protokoll die Schwierigkeit. Auf diese Weise passt sich die Validierungszeit selbst an, um eine konstante Rate zu halten — durchschnittlich wird ein Block alle 15 Sekunden erstellt.
Kommen wir zu einem der komplexesten Teile des Ethereum-Protokolls: Die Ausführung einer Transaktion. Stell dir vor, eine Transaktion zur Verarbeitung in das zu Ethereum Netzwerk senden. Was passiert, damit der Zustand von Ethereum geändert wird, um deine Transaktion einzubeziehen?
Zuallererst müssen alle Transaktionen einen Reihe von Anforderungen erfüllen, um ausgeführt zu werden. Dazu gehören:
Wenn die Transaktion alle oben genannten Voraussetzungen für die Gültigkeit erfüllt, gehen wir zum nächsten Schritt. Zuerst ziehen wir die Vorabkosten für die Ausführung vom Saldo des Absenders ab und erhöhen die Nonce des Absenders um 1, um die aktuelle Transaktion zu berücksichtigen. An diesem Punkt können wir das verbleibende Gas als Gesamtgaslimit für die Transaktion, abzüglich des verwendeten intrinsischen Gas, errechnen.
Als nächstes wird die Transaktion ausgeführt. Während der Ausführung einer Transaktion verfolgt Ethereum den „Teilzustand“. Dieser Unterstatus ist eine Möglichkeit, Informationen aufzuzeichnen, die während der Transaktion gesammelt wurden, die unmittelbar nach Abschluss der Transaktion benötigt werden. Hier sind konkret enthalten:
Als nächstes werden die verschiedenen Berechnungen bearbeitet, die für die Transaktion erforderlich sind. Sobald alle erforderlichen Schritte für die Transaktion bearbeitet wurden und vorausgesetzt es gibt keinen ungültigen Zustand, wird der Zustand finalisiert, in dem die Menge von zu erstattendem ungenutzten Gas an den Absender ermittelt wird. Neben dem ungenutzten Gas wird dem Absender auch ein Teil der oben beschriebenen „Refund balance“ zurückerstattet. Sobald der Absender erstattet wurde:
Schließlich haben wir den neuen Zustand und eine Reihe von Logs erhalten, die von der Transaktion erstellt wurden. Da wir nun über die Grundlagen der Transaktionsausführung besprochen haben, sollten wir uns einige der Unterschiede zwischen vertragserzeugenden Transaktionen (contract-creating transactions) und Nachrichtenanrufen (message calls) ansehen.
Erinnere dich daran, dass es in Ethereum zwei Arten von Konten gibt: Vertragskonten und externe Konten. Wenn wir sagen, dass eine Transaktion „vertragserzeugend“ ist, meinen wir, dass der Zweck der Transaktion die Schaffung eines neuen Kontraktskontos ist. Um ein neues Vertragskonto zu erstellen, geben wir zuerst die Adresse des neuen Kontos mit einer speziellen Formel an. Dann initialisieren wir das neue Konto durch:
Sobald wir das Konto initialisieren, können wir tatsächlich das Konto, unter Verwendung des Init-Codes, der mit der Transaktion gesendet wurde (siehe Abschnitt „Transaktion und Nachrichten“ für eine Aktualisierung des Init-Codes), erstellen. Was während der Ausführung dieses Init-Codes geschieht, ist unterschiedlich. Abhängig vom Konstruktor des Vertrages kann es den Speicher des Kontos aktualisieren, andere Vertragskonten erstellen, andere Nachrichtenanrufe tätigen, etc. Wenn der Code zur Initialisierung eines Vertrages ausgeführt wird, wird Gas verwendet. Die Transaktion darf nicht mehr Gas verbrauchen als das verbleibende Gas. Wenn dies der Fall ist, wird die Ausführung auf eine out-of-gas-Ausnahme (OOG) treffen und beendet. Wenn die Transaktion aufgrund einer out-of-gas-Ausnahme beendet wird, ändert sich der Zustand zum Punkt unmittelbar vor der Transaktion zurück. Dem Absender wird das Gas nicht zurückerstattet, das vor dem Auslaufen ausgegeben wurde. Buuhuu. Wenn der Absender jedoch einen Ether-Wert mit der Transaktion gesendet hat, wird der Ether-Wert zurückerstattet, auch wenn die Vertragserstellung fehlschlägt. Puh! Wird der Initialisierungscode erfolgreich ausgeführt, werden die Endkosten für die Erstellung von Verträgen bezahlt. Dies sind Speicherkosten (storage cost) und proportional zur Größe des Code des erstellten Vertrages (auch hier ist nichts umsonst!) Wenn nicht genügend Gas übrig ist, um diese Endkosten zu bezahlen, erklärt die Transaktion erneut eine out-of-gas-Ausnahme und bricht ab. Wenn alles gut geht, und wir so weit ohne Ausnahmen gekommen sind, wird jedes verbleibende ungenutzte Gas an den ursprünglichen Absender der Transaktion zurückerstattet und dem geänderten Zustand wird nun erlaubt zu bleiben! Hurra!
Die Ausführung eines Nachrichtenaufrufs ähnelt der einer Vertragserstellung mit einigen Unterschieden. Die Ausführung eines Nachrichtenanrufs enthält keinen Init-Code, da keine neuen Konten erstellt werden. Er kann jedoch Eingangsdaten enthalten, wenn diese Daten vom Transaktionsabsender zur Verfügung gestellt wurden. Nach der Ausführung haben Nachrichtenaufrufe auch eine zusätzliche Komponente, die die Ausgabedaten enthält, die verwendet werden, wenn eine spätere Ausführung diese Daten benötigt. Wie auch bei der Vertragserstellung zutreffend, wenn die Ausführung eines Nachrichtenanfrufs beendet wird, weil ihm kein Gas mehr zur Verfügung steht oder weil die Transaktion ungültig ist (bspw. Stapelüberlauf, ungültiges Sprungziel oder ungültige Anweisung), wird kein verwendetes Gas an den ursprünglichen Anrufer zurückerstattet. Stattdessen wird das gesamte verbleibende ungenutzte Gas verbraucht und der Zustand wird unmittelbar auf den Punkt vor dem Transfer zurückgesetzt. Bis zum letzten Update von Ethereum gab es keine Möglichkeit, die Ausführung einer Transaktion zu stoppen oder rückgängig zu machen, ohne dass das System das vom Versender angegebene Gas verbraucht hat. Beispielsweise hast du einen Vertrag verfasst, der einen Fehler verursachte, wenn ein Anrufer nicht berechtigt war, eine Transaktion durchzuführen. In früheren Versionen von Ethereum würde das verbleibende Gas weiterhin verbraucht und kein Gas an den Absender zurückerstattet. Aber das Byzantium-Update enthält einen neuen „Zurücksetzen“-Code, der es einem Vertrag erlaubt, die Ausführung zu stoppen und Zustandsänderungen rückgängig zu machen, ohne das verbleibende Gas zu verbrauchen. Zudem gibt es die Möglichkeit, einen Grund für die fehlgeschlagene Transaktion zurückzugeben. Beendet eine Transaktion durch ein Zurückfallen auf den vorherigen Zustand, dann wird das ungenutzte Gas an den Absender zurückgegeben.
Bisher haben wir erfahren, welche Schritte für die Ausführung einer Transaktion von Anfang bis Ende erforderlich sind. Jetzt werden wir einen Blick darauf werfen, wie die Transaktion tatsächlich innerhalb der VM ausgeführt wird. Der Teil des Protokolls, der die Abwicklung der Transaktionen regelt, ist die virtuelle Maschine von Ethereum, die als Ethereum Virtual Machine (EVM) bekannt ist. Die EVM ist eine vollständige virtuelle Turing-Maschine, wie zuvor definiert. Die einzige Einschränkung der EVM, die eine typische vollständige Turing-Maschine nicht besitzt, ist dass die EVM an sich an Gas gebunden ist. Die Gesamtberechnung, die durchgeführt werden kann, ist also durch die zur Verfügung gestellte Menge an Gas begrenzt.
Quelle: CMU Darüber hinaus hat die EVM eine stapelbasierte Architektur. Eine Stapelmaschine ist ein Computer, der einen Last-in, first-out-Stapel verwendet, um temporäre Werte zu halten. Die Größe jedes Stapelgegenstandes in der EVM beträgt 256 Bit, der Stapel hat eine maximale Größe von 1024. Die EVM verfügt über einen Hauptspeicher, in dem Elemente als wortadressierte Byte-Arrays gespeichert werden. Der Hauptspeicher ist volatil, was bedeutet, dass er nicht permanent ist. Die EVM verfügt auch über Speicher. Im Gegensatz zum Hauptspeicher ist die Speicherung nicht flüchtig und wird als Teil des Systemzustands gehalten. Die EVM speichert Programmcode separat in einem virtuellen ROM ab, auf den nur über spezielle Anweisungen zugegriffen werden kann. Auf diese Weise unterscheidet sich die EVM von der typischen von Neumann-Architektur, in der Programmcode im Hauptspeicher oder Speicher gespeichert wird.
Die EVM hat auch eine eigene Sprache: „EVM Bytecode. Wenn ein Programmierer wie du oder ich Smart Contracts für Ethereum entwickeln, schreiben wir den Code typischerweise in einer höheren Sprache wie Solidity. Wir können dies dann zu EVM-Bytecode kompilieren, den die EVM verstehen kann. Okay, nun zur Ausführung. Vor der Ausführung einer bestimmten Berechnung stellt der Prozessor sicher, dass die folgenden Informationen verfügbar und gültig sind:
Zu Beginn der Ausführung sind Hauptspeicher und Stapel leer und der Programmzähler ist null.
PC: 0 STACK: [] MEM: [], STORAGE: {}
Die EVM führt dann die Transaktion rekursiv aus und berechnet für jede Schleife den Systemzustand und den Maschinenzustand. Der Systemzustand ist schlichtweg Ethereums globaler Zustand. Der Maschinenzustand besteht aus:
Stapelelemente werden vom Teil ganz links in der Serie hinzugefügt oder entfernt. Bei jedem Zyklus wird die entsprechende Gasmenge vom verbleibenden Gas abgezogen und der Programmzähler wird erhöht. Am Ende jeder Schleife gibt es drei Möglichkeiten:
Angenommen die Ausführung trifft keinen Ausnahmezustand und erreicht einen „kontrollierten“ oder einen normalen Stillstand, dann erzeugt die Maschine den resultierenden Zustand, das verbleibende Gas nach dieser Ausführung, den angesammelten Unterzustand und die daraus resultierende Ausgabe. Puh. Wir sind mit einem der komplexesten Teile von Ethereum fertig. Auch wenn man diesen Teil nicht vollständig begreift, ist das okay. Du musst die nackten Ausführungsdetails nicht wirklich verstehen, es sei denn, du arbeitest auf einem sehr tiefen Niveau.
Schauen wir uns schließlich an, wie ein Block mit vielen Transaktionen finalisiert wird. Wenn wir „finalisiert“ sagen, kann das zwei verschiedene Dinge bedeuten, je nachdem, ob der Block neu ist oder bereits existiert. Wenn es ein neuer Block ist, beziehen wir uns auf den Prozess, der zum Mining dieses Blocks erforderlich ist. Wenn es ein bestehender Block ist, sprechen wir über den Prozess der Validierung des Blocks. In beiden Fällen gibt es vier Anforderungen, damit ein Block „finalisiert“ wird:
1) Validiere (oder, wenn Mining, bestimme) Verwandte Jeder Verwandte-Block innerhalb des Block-Headers muss ein gültige Header sein und innerhalb der sechsten Generation des aktuellen Blocks liegen.
2) Validiere (oder, wenn Mining, bestimme) Transaktionen Die gasUsed-Nummer im Block muss dem kumulativen Gas entsprechen, das von den Transaktionen verwendet wird, die im Block gelistet sind. (Erinnere dich, dass wir bei der Ausführung einer Transaktion den Blockgaszähler verfolgen , der den Überblick über das gesamte Gas aller Transaktionen im Block hält).
3) Gib Belohnungen aus (nur beim Mining) Die Empfängeradresse erhält 5 Ether für das Minen des Blocks. (Mit Inkrafttreten des Ethereum-Vorschlags EIP-649 wird diese Prämie von 5 ETH bald auf 3 ETH reduziert werden). Zusätzlich erhält der aktuelle Blockempfänger für jeden Verwandten 1/32 der aktuellen Blockbelohnung. Schließlich erhält auch der Nutznießer jedes Verwandten-Blocks eine bestimmte Belohnung (es gibt eine spezielle Formel für die Berechnung).
4) Verifiziere den (oder, wenn Mining, berechne den gültigen) Zustand und die Nonce Stelle sicher, dass alle Transaktionen und resultierenden Zustandsänderungen angewendet werden und definiere den neuen Block als den Zustand, nachdem die Blockbelohnung auf den resultierenden Zustand der endgültigen Transaktion angewendet wurde. Die Verifizierung erfolgt durch Überprüfung dieses Endzustands gegen den Zustand, der im Header gespeichert ist.
Der Abschnitt „Blöcke“ hat kurz das Konzept der Block-Schwierigkeit angesprochen. Der Algorithmus, der die Block-Schwierigkeit steuert, nennt sich Proof of Work (PoW). Der Proof-of-Work-Algorithmus von Ethereum heißt „Ethash“ (früher Dagger-Hashimoto genannt). Der Algorithmus ist formell definiert als:
Wo m der mixHash* ist, ist* n die Nonce, Hn ist der Header des neuen Blocks (ausgeschlossen der Nonce und der mixHash Komponenten, die berechnet werden müssen), Hn ist die Nonce des Block Headers, und d ist der DAG, der einen großen Datensatz darstellt. In dem “Blocks” Abschnitt, haben wir über die verschiedenen Dinge gesprochen, die im Block-Header existieren. Zwei dieser Komponenten wurden mixHash und nonce genannt. Wie du dich vielleicht erinnern kannst:
Die PoW-Funktion dient der Bewertung dieser beiden Elemente. Wie genau der mixHash und die Nonce mit der PoW-Funktion berechnet werden, ist ziemlich kompliziert, und etwas, in das wir in einen separaten Beitrag tiefer eintauchen können. Aber vereinfacht gesagt funktioniert es wie folgt: Für jeden Block wird ein „Seed“ berechnet. Dieser Seed unterscheidet sich in jedem "Abschnitt" (epoch), wobei jeder Abschnitt 30.000 Blöcke lang ist. Für den ersten Abschnitt ist der Seed der Hash einer Serie von 32 Bytes von Nullen. Für jeden folgenden Abschnitt ist es der Hash des vorherigen Seed-Hash. Mit diesem Seed kann eine Node einen pseudo-zufälligen „Cache“ berechnen. Dieser Cache ist unglaublich nützlich, da er das Konzept der „Light Nodes“ ermöglicht, was wir zuvor in diesem Artikel diskutiert haben. Der Zweck von Light Nodes ist es, bestimmten Nodes die Möglichkeit zu geben, eine Transaktion effizient zu überprüfen, ohne die Last der Speicherung des gesamten Blockchain Datensatzes. Eine Light Node kann die Gültigkeit einer Transaktion basierend einzig mit Hilfe dieses Caches überprüfen, da der Cache den spezifischen Block neu generieren kann, den er zur Überprüfung benötigt. Mit dem Cache kann eine Node den DAG-„Datensatz“ generieren, wobei jedes Element im Datensatz von einer kleinen Anzahl von pseudo-zufällig ausgewählten Elementen aus dem Cache abhängt. Um ein Miner zu sein, musst du diesen vollen Datensatz generieren. Alle Full Clients und Miner speichern diesen Datensatz, und der Datensatz wächst linear mit der Zeit an. Miner können dann zufällige Slices des Datensatzes durch eine mathematische Funktion zusammenfassen, um sie zu einem “mixHash” zusammenzufassen.
Ein Miner wird wiederholt einen mixHash erzeugen, bis die Ausgabe unter der gewünschten Ziel- Nonce ist. Wenn die Ausgabe diese Anforderung erfüllt, gilt diese Nonce als gültig und der Block kann der Kette hinzugefügt werden. Mining als Sicherheitsmechanismus Insgesamt gesehen soll das PoW in einer kryptographisch sicheren Art und Weise beweisen, dass eine bestimmte Menge an Berechnungen verwendet wurde, um einige Ausgaben zu generieren (dh. die Nonce). Das liegt daran, dass es keine bessere Möglichkeit gibt, eine Nonce zu finden, die unter dem erforderlichen Schwellenwert liegt, als alle Möglichkeiten aufzuzählen. Die Ausgaben der wiederholten Anwendung der Hashfunktion sind einheitlich verteilt, so dass wir sicher sein können, dass im Durchschnitt benötigte Zeit, eine solche Nonce zu finden, vom Schwierigkeitsgrad abhängt. Je höher die Schwierigkeit, desto länger dauert es, die Nonce zu lösen. Auf diese Weise gibt der PoW-Algorithmus dem Konzept der Schwierigkeit Bedeutung, die zur Durchsetzung der Blockchain-Sicherheit verwendet wird. Was verstehen wir unter Blockchain-Sicherheit? Es ist ganz einfach: Wir wollen eine Blockchain schaffen, der JEDER vertraut. Wie wir zuvor in diesem Beitrag diskutierten: Wenn mehr als eine Kette existierte, würden die Nutzer das Vertrauen verlieren, weil sie nicht in der Lage wären, vernünftig zu bestimmen, welche Kette die „gültige“ Kette wäre. Damit eine Gruppe von Benutzern den zugrundeliegenden Zustand akzeptiert, der in einer Blockchain gespeichert wird, brauchen wir eine einzige anerkannte Blockkette, an die eine Gruppe von Menschen glaubt. Das ist genau das, was der PoW-Algorithmus tut: Er stellt sicher, dass eine bestimmte Blockchain in der Zukunft anerkannt bleibt. Dies macht es für einen Angreifer unglaublich schwierig, neue Blöcke zu erstellen, die einen bestimmten Teil der Geschichte überschreiben (e. B. durch Löschung von Transaktionen oder durch Erzeugen von gefälschten Transaktionen) oder durch das Aufrechterhalten einer Fork. Um seinen Block zuerst validieren zu lassen, müsste ein Angreifer schneller als jeder andere im Netzwerk konsequent die Nonce lösen, so dass das Netzwerk glaubt, dass seine Kette die schwerste Kette ist (basierend auf den Grundsätzen des GHOST-Protokolls, das wir bereits erwähnt haben). Dies wäre unmöglich, wenn der Angreifer nicht mehr als die Hälfte der Mining-Power besäße. Ein Szenario, das als Majority 51% Attack bekannt ist.
Neben der Bereitstellung einer sicheren Blockchain ist PoW auch eine Möglichkeit, Reichtum an diejenigen zu verteilen, die ihre Berechnungen für diese Sicherheit aufwenden. Erinnere dich daran, dass ein Miner eine Belohnung für das Minen eines Blocks erhält, einschließlich:
Um sicherzustellen, dass der Einsatz des PoW-Konsensmechanismus für Sicherheit und Vermögensverteilung langfristig nachhaltig ist, strebt Ethereum danach, diese beiden Eigenschaften zu vermitteln:
Im Bitcoin-Blockchain-Netzwerk ist ein Problem in Bezug auf die oben genannten Eigenschaften, dass der PoW-Algorithmus eine SHA256-Hash-Funktion nutzt. Die Schwäche dieser Funktion besteht darin, dass sie mit spezialisierter Hardware, auch bekannt als ASICs, wesentlich effizienter gelöst werden kann. Um dieses Problem zu mildern, hat sich Ethereum entschieden, seinen PoW-Algorithmus (Ethhash) sequentiell Memory-Hard zu machen. Das bedeutet, dass der Algorithmus so konstruiert ist, dass die Berechnung der Nonce viel Hauptspeicher UND Bandbreite erfordert. Die großen Speicheranforderungen erschweren es einem Computer, seinen Speicher parallel zu nutzen, um mehrere Nonces gleichzeitig zu entdecken und die hohen Anforderungen an die Bandbreite machen es sogar einem superschnellen Computer schwer, mehrere Nonces gleichzeitig zu lösen. Dadurch wird das Risiko einer Zentralisierung reduziert und es entstehen mehr gleiche Wettbewerbsbedingungen für die Nodes, die die Überprüfung durchführen. Bedenke, dass Ethereum von einem PoW-Konsensmechanismus zu etwas übergeht, das als „Proof-of-Stake“ bezeichnet wird. Dies ist ein schwer zu behandelndes eigenes Thema, das wir hoffentlich in einem zukünftigen Beitrag ergründen können.
…Puh! Du hast es bis zum Ende geschafft. Hoffe ich doch?
Es gibt viel zu verdauen in diesem Beitrag, ich weiß. Wenn du mehrere Lesedurchgänge brauchst, um zu verstehen, was vor sich geht, ist das völlig in Ordnung. Ich habe persönlich das Ethereum Yellow Paper, das White Paper und verschiedene Teile der Code-Basis viele Male lesen müssen, bevor ich geschnallt habe, wie alles abläuft.
Dennoch hoffe ich, dass dir dieser Überblick hilfreich war. Wenn du Fehler findest, bitte ich dich, eine private Notiz zu schreiben oder sie direkt in den Kommentaren zu veröffentlichen. Ich schaue sie mir alle an, versprochen ;).
Und denke daran, ich bin auch nur einMensch (yep, es ist wahr) und mache Fehler. Ich habe mir die Zeit genommen, diesen Beitrag zum Wohle der Gemeinschaft zu schreiben, kostenlos. Sei also bitte konstruktiv in deinem Feedback, ohne unnötiges Niedermachen.
[1] https://github.com/ethereum/yellowpaper
Founder & CEO of TruStory. I have a passion for understanding things at a fundamental level and sharing it as clearly as possible.