Cryptocurrencies - Exam
Aufgabe 1)
Proof of Work (PoW) und Proof of Stake (PoS) sind zwei verschiedene Konsensmechanismen, die in Kryptowährungen verwendet werden. PoW erfordert umfangreiche Rechenleistung, um kryptografische Rätsel zu lösen und neue Blöcke zu erstellen, während PoS auf dem Besitz von Coins und deren „Einsetzen“ (Staking) basiert, um neue Blöcke zu validieren und zu erstellen. Hierbei verbraucht PoS deutlich weniger Energie als PoW.
a)
Vergleiche und kontrastiere die Energieeffizienz von Proof of Work und Proof of Stake. Nutzen Sie dabei reale Beispiele wie Bitcoin und Ethereum, um Ihre Argumentation zu unterstreichen. Diskutieren Sie die Auswirkungen auf die Umwelt und die langfristige Nachhaltigkeit beider Methoden.
Lösung:
Vergleich und Kontrast: Energieeffizienz von Proof of Work (PoW) und Proof of Stake (PoS)
- Grundlagen von PoW und PoS:
- Proof of Work (PoW): Dieser Konsensmechanismus erfordert, dass Miner komplexe kryptografische Rätsel lösen, um neue Blöcke zur Blockchain hinzuzufügen. Dieser Prozess benötigt erhebliche Rechenleistung und somit auch eine große Menge an Energie.
- Proof of Stake (PoS): Hier wird die Erstellung neuer Blöcke basierend auf dem Besitz und dem Einsatz (Staking) von Kryptowährungen durchgeführt. Teilnehmer setzen ihre Coins ein, um als Validatoren gewählt zu werden. Dieser Prozess ist wesentlich weniger energieaufwendig.
- Reale Beispiele:
- Bitcoin (PoW): Bitcoin, die bekannteste Kryptowährung, verwendet Proof of Work. Der Energieverbrauch des Bitcoin-Netzwerks ist enorm und soll laut einiger Schätzungen sogar den Jahresverbrauch ganzer Länder übersteigen. Im Jahr 2021 wurde berichtet, dass das Bitcoin-Netzwerk mehr als 100 Terawattstunden (TWh) pro Jahr verbraucht, was in etwa dem Energieverbrauch von Ländern wie den Niederlanden entspricht.
- Ethereum (PoS): Ethereum war ursprünglich auch PoW-basiert, hat jedoch begonnen, auf einen PoS-Mechanismus (bekannt als Ethereum 2.0) umzustellen. Ziel ist es, den Energieverbrauch drastisch zu senken. Studien deuten darauf hin, dass Ethereum 2.0 den Energieverbrauch um bis zu 99% reduzieren könnte, verglichen mit dem PoW-basierten Ethereum.
- Umweltauswirkungen:
- Proof of Work: Die hohe Energieanforderung von PoW führt zu einem erheblichen CO2-Fußabdruck. Vielerorts wird die benötigte Energie aus fossilen Brennstoffen gewonnen, was zur Verschlimmerung des Klimawandels beiträgt.
- Proof of Stake: PoS verbraucht wesentlich weniger Energie, da keine komplexen Rätsel gelöst werden müssen. Daher hat PoS einen deutlich geringeren CO2-Ausstoß und ist umweltfreundlicher.
- Langfristige Nachhaltigkeit:
- Proof of Work: Die Nachhaltigkeit von PoW wird stark durch den Energieverbrauch beeinträchtigt. Die zunehmende Anzahl an Minern und die steigende Komplexität der Rätsel führen zu einem kontinuierlichen Anstieg des Energieverbrauchs, was langfristig schwer zu rechtfertigen ist.
- Proof of Stake: Die wesentlich niedrigeren Energieanforderungen machen PoS zu einer nachhaltigeren und umweltfreundlicheren Alternative. Durch die Reduzierung des Energieverbrauchs bietet PoS eine praktikable Lösung zur Unterstützung langfristiger Umweltziele.
b)
Erläutern Sie die Funktionsweise von Proof of Stake und beschreiben Sie, wie der Staking-Prozess abläuft. Erklären Sie gleichzeitig, wie die Sicherheit im Netzwerk durch PoS aufrechterhalten wird. Nutzen Sie dabei konkrete Details von Ethereum nach der Umstellung auf Ethereum 2.0.
Lösung:
Funktionsweise von Proof of Stake (PoS)
- Grundprinzip: Proof of Stake basiert auf dem Besitz (Stake) und Einsatz von Kryptowährungen. Im Gegensatz zu Proof of Work, wo Miner Rechenleistung bereitstellen, setzen Teilnehmer im PoS ihre Coins ein, um als Validatoren ausgewählt zu werden, die dann neue Blöcke erstellen und Transaktionen validieren.
Der Staking-Prozess:
- Staking: Teilnehmer, die Validatoren werden möchten, müssen eine bestimmte Menge an Kryptowährung (Stake) als Sicherheit hinterlegen. Je mehr Coins ein Teilnehmer einsetzt, desto höher ist die Wahrscheinlichkeit, als Validator ausgewählt zu werden.
- Auswahl des Validators: Die Auswahl erfolgt algorithmisch und berücksichtigt den Einsatz (Stake) und andere Faktoren wie die Dauer, die die Coins eingesetzt wurden. Hierdurch wird eine faire und dezentralisierte Auswahl gewährleistet.
- Erstellung von Blöcken: Der gewählte Validator erstellt den nächsten Block in der Blockchain und validiert alle darin enthaltenen Transaktionen. Sobald der Block hinzugefügt wurde, erhalten sie eine Belohnung in Form von zusätzlichen Coins.
- Sicherheitsmaßnahmen: Validatoren, die ungültige oder manipulierte Blöcke erstellen, riskieren den Verlust eines Teils oder ihres gesamten Einsatzes (Slashing). Dieses Anreizsystem stellt sicher, dass Validatoren ehrlich agieren.
Sicherheit im Netzwerk durch PoS
- Anreize für ehrliches Verhalten: Da Validatoren ihre eigenen Coins riskieren, haben sie einen starken Anreiz, ehrlich und korrekt zu agieren, um die Integrität der Blockchain und ihre eigenen Investitionen zu schützen.
- Dezentralisation: Durch die mechanische und zufällige Auswahl von Validatoren, basierend auf ihrem Einsatz, wird eine zentrale Machtkonzentration vermieden. Dies erhöht die Robustheit und Sicherheit des Netzwerks.
- Vermeidung von 51%-Angriffen: In PoS wäre ein Angreifer, der versucht, die Blockchain mit 51% der eingesetzten Coins zu kontrollieren, extrem teuer. Da der Angreifer einen sehr hohen Einsatz bräuchte, wäre der Angriff kostenintensiver als in einem PoW-System.
Konkrete Details zu Ethereum nach der Umstellung auf Ethereum 2.0:
- Ethereum 2.0 und der Beacon Chain: Ethereum 2.0 nutzt die Beacon Chain, die als Taktgeber und Koordinator für das PoS-System fungiert. Validatoren melden sich an und setzen ihre Coins in der Beacon Chain ein.
- Staking-Anforderungen: Um ein Validator auf Ethereum 2.0 zu werden, muss ein Teilnehmer mindestens 32 ETH als Stake hinterlegen. Kleine Investoren können in Pools zusammenfassen, um die Staking-Mindestanforderung zu erfüllen.
- Sharding: Ethereum 2.0 plant die Einführung von Shard Chains, die die Last der Transaktionsverarbeitung verteilen und so die Skalierbarkeit verbessern. Validatoren werden Blöcke in verschiedenen Shards validieren, was die Effizienz weiter steigert.
- Sicherheitsaspekte: Die Sicherheitsarchitektur von Ethereum 2.0 beinhaltet strikte Protokolle und Mechanismen, um böswillige Aktivitäten zu erkennen und zu bestrafen (Slashing). Dies trägt dazu bei, die Integrität des Netzwerks aufrechtzuerhalten.
c)
Für das Proof of Work-System ist die Berechnung des Hashwertes entscheidend. Geben Sie die Formel für Proof of Work an und erklären Sie den Zusammenhang der Variablen in dieser Formel. Wenn der Zielschwellenwert (\textit{T}) 1.5 * 10^{10} beträgt und der berechnete Hashwert (\textit{H(x)}) 1.4 * 10^{10} ist, wird dieser neue Block akzeptiert? Begründen Sie Ihre Antwort mathematisch.
Lösung:
Die Formel für Proof of Work (PoW)
- Im Proof of Work-System basiert die Validierung eines neuen Blocks darauf, dass der berechnete Hashwert (\textit{H(x)}) einen bestimmten Zielschwellenwert (\textit{T}) erfüllt:
- \[ H(x) \leq T\]
Erklärung der Variablen:
- H(x): Dies ist der Hashwert des aktuellen Blocks, berechnet durch den Miner. Der Hashwert wird mittels eines kryptografischen Hash-Algorithmus (wie SHA-256) erstellt und repräsentiert eine eindeutige, konsistente Größe für den Block basierend auf dessen Inhalt und einem Nonce (einer zufälligen oder pseudozufälligen Zahl).
- T: Der Zielschwellenwert (Target) ist eine vorgegebene Zahl, die der berechnete Hashwert nicht überschreiten darf. Der Zielschwellenwert wird durch das Netzwerk reguliert, um die Schwierigkeit des Mining-Prozesses anzupassen. Je kleiner der Zielschwellenwert, desto schwieriger ist es, einen gültigen Block zu finden.
Mathematische Begründung anhand des Beispiels
- Gegeben:
- Der Zielschwellenwert (\textit{T}) beträgt 1.5 * 10^{10}.
- Der berechnete Hashwert (\textit{H(x)}) beträgt 1.4 * 10^{10}.
- Prüfung, ob der neue Block akzeptiert wird:
- Die Bedingung lautet:
- \[ H(x) \leq T\]
- Setzen wir die gegebenen Werte ein:
- \[ 1.4 * 10^{10} \leq 1.5 * 10^{10}\]
- Da 1.4 * 10^{10} kleiner als 1.5 * 10^{10} ist, erfüllt der berechnete Hashwert die Bedingung.
- Ergebnis: Ja, der neue Block wird akzeptiert, weil der berechnete Hashwert (\textit{H(x)}) kleiner oder gleich dem Zielschwellenwert (\textit{T}) ist.
d)
Berechne die Wahrscheinlichkeit, dass ein bestimmter Validator im Proof of Stake Netzwerk ausgewählt wird, wenn der Validator 5000 ETH besitzt und das gesamte Staking-Pool 1.000.000 ETH umfasst. Verwenden Sie die Formel \( P(V) = \frac{V_s}{V_t} \), wobei P(V) die Wahrscheinlichkeit ist, V_s die Anzahl der gestakten Coins des Validators und V_t der gesamte Staking-Pool ist.
Lösung:
Berechnung der Wahrscheinlichkeit, dass ein bestimmter Validator im Proof of Stake Netzwerk ausgewählt wird
Die Formel zur Berechnung der Wahrscheinlichkeit, dass ein bestimmter Validator ausgewählt wird, lautet:
\[ P(V) = \frac{V_s}{V_t} \]
Hierbei gilt:
- P(V): Wahrscheinlichkeit, dass der Validator ausgewählt wird.
- V_s: Anzahl der gestakten Coins des Validators.
- V_t: Gesamter Staking-Pool.
Gegebene Werte:
- Der Validator besitzt 5000 ETH, also ist \(V_s = 5000\).
- Der gesamte Staking-Pool umfasst 1.000.000 ETH, also ist \(V_t = 1.000.000\).
Berechnung der Wahrscheinlichkeit:
- Setzen wir die Werte in die Formel ein:
\[ P(V) = \frac{5000}{1.000.000} \]
- Berechnen wir das Verhältnis:
\[ P(V) = \frac{5000}{1.000.000} = 0,005 \]
- Die Wahrscheinlichkeit, dass dieser Validator ausgewählt wird, beträgt somit 0,005 oder 0,5%.
Aufgabe 2)
Betrachten wir den Prozess der Transaktionsvalidierung und Blockbildung in der Bitcoin-Blockchain. Der Prozess umfasst die Überprüfung der Gültigkeit von Transaktionen durch Miner und die Aufnahme dieser Transaktionen in gültige Blöcke, die der Blockchain hinzugefügt werden. Der Proof-of-Work (PoW)-Konsensmechanismus spielt eine entscheidende Rolle bei der Blockvalidierung. Miner erhalten für ihre Arbeit neue Bitcoins sowie Transaktionsgebühren. Jeder Block in der Bitcoin-Blockchain hat eine feste Größe von 1 MB und enthält einen Header sowie die Transaktionen. Der Block Header beinhaltet wichtige Informationen wie den Hash des vorherigen Blocks, einen Nonce, den Zeitstempel und den Merkle-Root-Hash. Merkle-Bäume ermöglichen eine effiziente Überprüfung von Transaktionen innerhalb eines Blocks, und es gibt algorithmische Überprüfungen, um das Problem der doppelten Ausgabe zu vermeiden.
a)
Nehmen wir an, Du bist ein Miner in der Bitcoin-Blockchain. Erläutere den Prozess des Proof-of-Work (PoW) und welche Schritte unternommen werden müssen, um einen neuen Block zu validieren und zur Blockchain hinzuzufügen. Welche Bedeutung hat dabei der Nonce?
Lösung:
Wenn Du ein Miner in der Bitcoin-Blockchain bist, umfasst der Prozess des Proof-of-Work (PoW) mehrere entscheidende Schritte, um einen neuen Block zu validieren und zur Blockchain hinzuzufügen:
- Transaktionssammlung: Zunächst sammelst Du eine Liste von Transaktionen, die zur Aufnahme in den neuen Block vorgesehen sind. Diese Transaktionen müssen noch validiert werden, um sicherzustellen, dass sie gültig sind (z.B. keine doppelten Ausgaben).
- Blockerstellung: Danach erstellt Dein Knoten (Node) einen neuen Block, der aus einem Block-Header und der Liste der gesammelten Transaktionen besteht. Der Block-Header enthält wichtige Informationen wie den Hash des vorherigen Blocks, den Zeitstempel, den Merkle-Root-Hash und den Nonce.
- Merkle-Root-Hash Berechnung: Mit Hilfe eines Merkle-Baums berechnest Du den Merkle-Root-Hash der Transaktionen. Dies ermöglicht eine effiziente Verifizierung der Transaktionen innerhalb des Blocks.
- Nonce-Wert Justierung: Der Nonce ist eine zufällige Zahl, die Du wiederholt anpasst, um den Hash-Wert des Block-Headers zu ändern. Der Zielwert (Schwierigkeitsgrad) für den Hash ist sehr niedrig, d.h. der Hash muss eine bestimmte Anzahl von führenden Nullen aufweisen.
- Hashing der Header-Daten: Du berechnest den Hash des Block-Headers. Diese Berechnung erfolgt viele Male mit unterschiedlichen Nonce-Werten, bis ein Hash gefunden wird, der die erforderliche Anzahl führender Nullen hat.
- Proof-of-Work lösen: Das Finden des passenden Nonce-Wertes für einen gültigen Hash stellt den eigentlichen Proof-of-Work dar. Dies erfordert enorme Rechenleistung und viele Versuche.
- Broadcasting und Validierung: Sobald Du einen gültigen Hash gefunden hast, wird der neue Block an das Netzwerk verteilt. Andere Knoten validieren den Block und prüfen u.a. den Proof-of-Work.
- Block zur Blockchain hinzufügen: Nach Validierung durch das Netzwerk wird der Block zur bestehenden Blockchain hinzugefügt. Der Mining-Prozess für den nächsten Block beginnt daraufhin von vorn.
Die Bedeutung des Nonce liegt darin, dass er als variable Eingabe dient, um den Hash-Wert des Block-Headers zu verändern. Das Ziel ist es, einen Hash-Wert zu finden, der den festgelegten Schwierigkeitsgrad-Kriterien entspricht. Der Nonce muss kontinuierlich angepasst werden, da nur so ein gültiger Hash gefunden werden kann.
b)
Gegeben sei ein Block mit den folgenden Transaktionen:
- Transaktion A mit Wert 1 BTC
- Transaktion B mit Wert 0.5 BTC
- Transaktion C mit Wert 0.2 BTC
Stelle den Merkle-Root-Hash für den Block dar. Gehe dabei Schritt für Schritt vor und nutze für den Hashvorgang die Hashfunktion SHA-256. Zeige auch, warum Merkle-Bäume effizient für die Verifizierung von Transaktionen sind.
Lösung:
Um den Merkle-Root-Hash für einen Block mit den gegebenen Transaktionen zu berechnen, gehen wir Schritt für Schritt vor:
- Transaktion A mit Wert 1 BTC
- Transaktion B mit Wert 0.5 BTC
- Transaktion C mit Wert 0.2 BTC
Für den Hashvorgang verwenden wir die Hashfunktion SHA-256. Hier sind die Schritte:
- Hashing der einzelnen Transaktionen:
- Transaktion A: Hash(A) = SHA-256(SHA-256(A))
- Transaktion B: Hash(B) = SHA-256(SHA-256(B))
- Transaktion C: Hash(C) = SHA-256(SHA-256(C))
- Paarweise Kombination und erneutes Hashing:
- Da wir eine ungerade Anzahl von Transaktionen haben, duplizieren wir Transaktion C, um eine gerade Anzahl zu erhalten:Hash(C) = SHA-256(SHA-256(C))
- Jetzt kombinieren wir die Paare:
- Kombiniere Hash(A) und Hash(B):Hash(AB) = SHA-256(SHA-256(Hash(A) + Hash(B)))
- Kombiniere Hash(C) und Hash(C):Hash(CC) = SHA-256(SHA-256(Hash(C) + Hash(C)))
- Merkle-Root-Hash berechnen:
- Kombiniere Hash(AB) und Hash(CC):Merkle-Root-Hash = SHA-256(SHA-256(Hash(AB) + Hash(CC)))
Nun, warum sind Merkle-Bäume effizient für die Verifizierung von Transaktionen?
- Effiziente Verifizierung: Merkle-Bäume ermöglichen es, mit nur wenigen Hash-Berechnungen (logarithmisch zu der Anzahl der Transaktionen) die Echtheit jeder Transaktion nachzuweisen. Man benötigt nur den Merkle-Root-Hash und eine Hash-Pfad-Zweigstruktur zur betroffenen Transaktion, um sie zu verifizieren.
- Sicherheit: Jeder Knoten (Elternknoten) repräsentiert den Hash-Wert seiner untergeordneten Knoten. Eine Änderung in einer Transaktion führt somit zu einer Kaskadenänderung bis zum Merkle-Root-Hash, wodurch Manipulationen leicht erkennbar sind.
- Ressourcenschonend: Statt alle Transaktionen erneut zu prüfen, sobald eine einzelne verifiziert werden muss, reduziert der Merkle-Baum den Aufwand erheblich, indem nur relevante Teile des Baums überprüft werden.
Aufgabe 3)
Die Ethereum Virtual Machine (EVM) ist die Laufzeitumgebung für Smart Contracts auf der Ethereum-Blockchain. Sie führt Bytecode aus, der aus einer höheren Programmiersprache wie Solidity kompiliert wurde, und gewährleistet die Konsistenz des Zustands der Blockchain. Die EVM verwendet ein Gasmodell als Maß für den Rechenaufwand, wobei das Gaslimit und der Gaspreis die Ausführungskosten beeinflussen. Zustandsübergänge und Transaktionen werden in einer Sandbox-Umgebung deterministisch ausgeführt, was bedeutet, dass bei identischen Eingaben immer die gleichen Ausgaben entstehen. Programme laufen in isolierten Umgebungen, um die Sicherheit und Integrität der Blockchain zu gewährleisten. Die EVM ist stack-basiert und arbeitet mit einer 256-bit-Wortgröße, wobei typische Befehle wie \texttt{PUSH}, \texttt{POP}, und \texttt{ADD} verwendet werden.
a)
Erkläre den Unterschied zwischen deterministischen und nicht-deterministischen Systemen. Warum ist Determinismus wichtig für die Ausführung von Smart Contracts in der EVM?
Lösung:
Unterschied zwischen deterministischen und nicht-deterministischen Systemen
- Deterministische Systeme: Ein System ist deterministisch, wenn es bei gleichen Eingabewerten immer die gleichen Ausgabewerte und Zustandsübergänge liefert. Das bedeutet, dass der Zustand des Systems vollständig durch die Eingaben und den aktuellen Zustand bestimmt ist. Es gibt keine Zufälligkeit oder Unvorhersehbarkeit in der Ausführung.
- Nicht-deterministische Systeme: Ein nicht-deterministisches System hingegen kann unterschiedliche Ausgaben oder Zustandsübergänge bei gleichen Eingaben erzeugen. Dies kann durch Zufallsereignisse oder durch externe Einflüsse (wie Zeit, Sensordaten, etc.) verursacht werden. Die Ausführung ist daher nicht vorhersehbar.
Warum ist Determinismus wichtig für die Ausführung von Smart Contracts in der EVM?
- Konsistenz und Reproduzierbarkeit: In der Ethereum Virtual Machine (EVM) ist Determinismus essenziell, um sicherzustellen, dass jeder Knoten im Netzwerk die gleichen Zustandsübergänge für eine gegebene Transaktion berechnet. Dies gewährleistet, dass der Zustand der Blockchain konsistent bleibt, egal welcher Knoten die Transaktion überprüft.
- Verifizierung und Sicherheit: Deterministische Ausführung ist wichtig für die Verifizierung von Smart Contracts. Da Smart Contracts oft finanzielle Transaktionen verwalten, ist es notwendig zu gewährleisten, dass sie auf jedem Knoten das gleiche Ergebnis liefern. Dies schützt vor Manipulation und gewährleistet die Integrität der Blockchain.
- Gasmodelle und Ressourcenverbrauch: Die Berechnung der benötigten Ressourcen (Gas) für die Ausführung eines Smart Contracts basiert auf deterministischen Regeln. Wenn die Ausführung nicht deterministisch wäre, könnten die berechneten Gaswerte variieren, was zu Inkonsistenzen und potenziellen Angriffspunkten führen könnte.
- Debugging und Fehlersuche: Deterministische Systeme erleichtern auch das Debugging und die Fehlersuche, da Entwickler sicher sein können, dass sie reproduzierbare Ergebnisse erhalten, wenn sie identische Eingabewerte und Zustände verwenden.
b)
Angenommen, ein Smart Contract enthält den folgenden Bytecode zur Addition zweier Zahlen:
PUSH1 0x02 PUSH1 0x03 ADD
Erläutere Schritt für Schritt, wie dieser Bytecode von der EVM ausgeführt wird und welches Ergebnis im Stack verbleibt.
Lösung:
Schritt-für-Schritt-Ausführung des Bytecodes durch die EVM
Der gegebene Bytecode ist:
PUSH1 0x02PUSH1 0x03ADD
Dies sind die Anweisungen, die nacheinander von der EVM ausgeführt werden. Im Folgenden sind die Schritte im Detail beschrieben:
- PUSH1 0x02
- Die erste Anweisung ist
PUSH1 0x02
, die die Zahl 0x02 (2 in Dezimal) auf den Stack legt. - Nach der Ausführung sieht der Stack folgendermaßen aus:
Stack: [2]
PUSH1 0x03 - Die zweite Anweisung ist
PUSH1 0x03
, die die Zahl 0x03 (3 in Dezimal) auf den Stack legt. - Nach der Ausführung sieht der Stack folgendermaßen aus:
Stack: [3, 2]
ADD - Die dritte Anweisung ist
ADD
, die die obersten beiden Werte vom Stack nimmt, sie addiert und das Ergebnis zurück auf den Stack legt. - Hier sind die Schritte der
ADD
-Operation im Detail: - Der Wert 3 wird vom Stack genommen.
- Der Wert 2 wird vom Stack genommen.
- Die Werte werden addiert: 3 + 2 = 5.
- Das Ergebnis (5) wird zurück auf den Stack gelegt.
- Nach der Ausführung sieht der Stack folgendermaßen aus:
Stack: [5]
Endergebnis
Nach der Ausführung des gesamten Bytecodes verbleibt die Zahl 5 im Stack.
c)
Wie beeinflussen Gaslimit und Gaspreis die Ausführung eines Smart Contracts? Berechne die Gesamtkosten in Ether für einen Smart Contract, der insgesamt 15000 Gas verbraucht, wenn der Gaspreis bei 20 Gwei liegt. Hinweis: 1 Gwei = \(10^{-9} \) Ether.
Lösung:
Gaslimit und Gaspreis in der Ausführung von Smart Contracts
- Gaslimit: Das Gaslimit legt die maximale Menge an Gas fest, die bei der Ausführung eines Smart Contracts verbraucht werden darf. Es handelt sich um eine Obergrenze, die der Nutzer setzt, um zu verhindern, dass eine Transaktion unendlich viel Gas verbraucht. Wenn das Gaslimit erreicht ist, bevor die Ausführung abgeschlossen ist, wird die Transaktion abgebrochen und alle Änderungen rückgängig gemacht.
- Gaspreis: Der Gaspreis ist der Betrag, den der Nutzer bereit ist, pro Gas-Einheit zu zahlen. Er wird in Gwei angegeben, wobei 1 Gwei = 10^{-9} Ether ist. Der Gaspreis beeinflusst, wie schnell eine Transaktion von den Minern bestätigt wird, da höhere Gaspreise den Minern mehr Anreiz bieten, die Transaktion zuerst zu verarbeiten.
Berechnung der Gesamtkosten in Ether
Um die Gesamtkosten eines Smart Contracts zu berechnen, der 15000 Gas verbraucht und bei einem Gaspreis von 20 Gwei ausgeführt wird, verwenden wir die folgende Formel:
Gesamtkosten = Gasverbrauch × Gaspreis
Wobei:
- Gasverbrauch = 15000 Gas
- Gaspreis = 20 Gwei = 20 × 10^{-9} Ether
Die Berechnung im Detail:
- Gasverbrauch = 15000 Gas
- Gaspreis in Ether = 20 Gwei = 20 × 10^{-9} Ether = 0,000000020 Ether
- Gesamtkosten = 15000 × 0,000000020 Ether = 0,0003 Ether
Endergebnis
Die Gesamtkosten für die Ausführung des Smart Contracts betragen 0,0003 Ether.
d)
Ein Smart Contract benötigt den Zugriff auf einen anderen Vertrag, um bestimmte Daten zu lesen. Wie interagiert die EVM sicher mit anderen Verträgen? Erläutere die Rolle isolierter Umgebungen und definierter Schnittstellen in diesem Kontext.
Lösung:
Wie die EVM sicher mit anderen Verträgen interagiert
Um die Sicherheit und Integrität der Blockchain zu gewährleisten, muss die EVM sicher und zuverlässig mit anderen Smart Contracts interagieren können. Hier spielen isolierte Umgebungen und definierte Schnittstellen eine wichtige Rolle. Im Folgenden wird erläutert, wie dies erreicht wird:
- Isolierte Umgebungen:
- Jeder Smart Contract in der EVM wird in einer isolierten Umgebung, auch Sandbox genannt, ausgeführt.
- Diese Isolation verhindert, dass ein fehlerhafter oder bösartiger Vertrag den Zustand anderer Verträge direkt beeinflusst. Jeder Vertrag hat seinen eigenen Speicherbereich, und direkte Speicherzugriffe auf andere Verträge sind nicht möglich.
- Wenn ein Vertrag eine Funktion eines anderen Vertrags aufruft, wird dies als eine Art „Unterauftrag“ behandelt. Der aufrufende Vertrag wartet auf das Ergebnis des Aufrufs und kann den Zustand des anderen Vertrags nicht direkt ändern.
- Definierte Schnittstellen:
- Um mit anderen Verträgen zu interagieren, verwendet die EVM definierte Schnittstellen. Diese Schnittstellen spezifizieren, welche Funktionen verfügbar sind und welche Parameter sie erwarten.
- Solidity, eine der Hauptprogrammiersprachen für Smart Contracts auf Ethereum, unterstützt die Definition von Schnittstellen durch das Schlüsselwort
interface
. Hiermit können Entwickler sicherstellen, dass nur definierte Methoden aufgerufen werden, wodurch die Interaktion sicherer und kontrollierter wird. - Wenn ein Vertrag eine externe Funktion aufruft, verwendet die EVM den Bytecode der aufgerufenen Funktion und übergibt die erforderlichen Parameter. Anschließend führt der andere Vertrag die Funktion aus und liefert das Ergebnis zurück.
Zusammenfassung
Durch die Verwendung von isolierten Umgebungen und definierten Schnittstellen stellt die EVM sicher, dass Smart Contracts sicher und deterministisch miteinander interagieren können. Diese Mechanismen schützen die Integrität der Blockchain und verhindern, dass schädlicher Code andere Verträge beeinträchtigt.
Aufgabe 4)
Programmiersprachen für Smart Contracts (z.B. Solidity)
Sprachen zur Umsetzung und Ausführung von Smart Contracts, wie z.B. Solidity auf der Ethereum-Plattform:
a)
Betrachte das folgende Beispiel eines Solidity Smart Contracts:
pragma solidity ^0.8.0;contract SimpleStorage { uint256 storedData; function set(uint256 x) public { storedData = x; } function get() public view returns (uint256) { return storedData; }}
- Beschreibe die Funktion des Smart Contracts, SimpleStorage. Welche Funktionalitäten bietet dieser Vertrag?
- Erkläre die Bedeutung der Sichtbarkeitsmodifikatoren public und view im Kontext dieses Contracts.
Lösung:
Betrachten wir das Beispiel eines Solidity Smart Contracts mit dem Namen SimpleStorage:
pragma solidity ^0.8.0;contract SimpleStorage { uint256 storedData; function set(uint256 x) public { storedData = x; } function get() public view returns (uint256) { return storedData; }}
- Beschreibung der Funktion des Smart Contracts SimpleStorage: Der Smart Contract SimpleStorage bietet eine einfache Möglichkeit, einen einzelnen ganzzahligen Wert zu speichern und zu lesen. Er verfügt über zwei Hauptfunktionen:
- set(uint256 x): Diese Funktion ermöglicht es, einen ganzzahligen Wert (x) zu speichern. Der gespeicherte Wert wird in der Variablen storedData abgelegt.
- get(): Diese Funktion ermöglicht es, den aktuell gespeicherten Wert der Variable storedData abzurufen und zurückzugeben.
- Erklärung der Sichtbarkeitsmodifikatoren public und view:
- public: Der Modifikator public bedeutet, dass die Funktion sowohl extern, d.h. von außerhalb des Contracts, als auch intern, von innerhalb des Contracts, aufgerufen werden kann. In unserem Beispiel können sowohl set(uint256 x) als auch get() von jedem, der Zugriff auf den Vertrag hat, aufgerufen werden.
- view: Der Modifikator view zeigt an, dass die betreffende Funktion keine Zustandsänderungen auf der Blockchain vornehmen wird. Dies bedeutet, dass die get() Funktion nur Daten lesen, jedoch keine Daten schreiben oder verändern kann. Sie ist darauf beschränkt, den aktuellen Zustand des Vertrags zu lesen und diesen zurückzugeben.
b)
Betrachte den folgenden Smart Contract und beantworte die damit verbundenen Fragen:
pragma solidity ^0.8.0;contract Auction { address payable public beneficiary; uint public auctionEndTime; address public highestBidder; uint public highestBid; mapping(address => uint) pendingReturns; bool ended; event AuctionEnded(address winner, uint amount); modifier onlyBeforeEnd() { require(block.timestamp < auctionEndTime); _; } modifier onlyNotOwner() { require(msg.sender != beneficiary); _; } constructor( uint _biddingTime, address payable _beneficiary ) { beneficiary = _beneficiary; auctionEndTime = block.timestamp + _biddingTime; } function bid() public payable onlyNotOwner onlyBeforeEnd { require(msg.value > highestBid); if (highestBid != 0) { pendingReturns[highestBidder] += highestBid; } highestBidder = msg.sender; highestBid = msg.value; } function withdraw() public { uint amount = pendingReturns[msg.sender]; require(amount > 0); pendingReturns[msg.sender] = 0; payable(msg.sender).transfer(amount); } function auctionEnd() public { require(block.timestamp >= auctionEndTime); require(!ended); ended = true; emit AuctionEnded(highestBidder, highestBid); beneficiary.transfer(highestBid); } }
- Analysiere die bid-Funktion des Auction-Smart Contracts. Welche Bedingungen müssen erfüllt sein, damit ein Gebot erfolgreich platziert werden kann?
- Erkläre die Funktionsweise und den Zweck der withdraw-Funktion im Auction-Smart Contract.
Lösung:
Betrachte den folgenden Smart Contract und beantworte die damit verbundenen Fragen:
pragma solidity ^0.8.0;contract Auction { address payable public beneficiary; uint public auctionEndTime; address public highestBidder; uint public highestBid; mapping(address => uint) pendingReturns; bool ended; event AuctionEnded(address winner, uint amount); modifier onlyBeforeEnd() { require(block.timestamp < auctionEndTime); _; } modifier onlyNotOwner() { require(msg.sender != beneficiary); _; } constructor( uint _biddingTime, address payable _beneficiary ) { beneficiary = _beneficiary; auctionEndTime = block.timestamp + _biddingTime; } function bid() public payable onlyNotOwner onlyBeforeEnd { require(msg.value > highestBid); if (highestBid != 0) { pendingReturns[highestBidder] += highestBid; } highestBidder = msg.sender; highestBid = msg.value; } function withdraw() public { uint amount = pendingReturns[msg.sender]; require(amount > 0); pendingReturns[msg.sender] = 0; payable(msg.sender).transfer(amount); } function auctionEnd() public { require(block.timestamp >= auctionEndTime); require(!ended); ended = true; emit AuctionEnded(highestBidder, highestBid); beneficiary.transfer(highestBid); } }
- Analyse der bid-Funktion des Auction-Smart Contracts: Die bid-Funktion ermöglicht es Benutzern, ein Gebot innerhalb der Auktion abzugeben. Damit ein Gebot erfolgreich platziert werden kann, müssen folgende Bedingungen erfüllt sein:
- Die Funktion muss vor dem Endzeitpunkt der Auktion aufgerufen werden, dies wird durch den onlyBeforeEnd Modifikator sichergestellt, der überprüft, ob die aktuelle Zeit (
block.timestamp
) kleiner als auctionEndTime
ist. - Derjenige, der das Gebot abgibt, darf nicht der Besitzer (beneficiary) der Auktion sein. Dies wird durch den onlyNotOwner Modifikator sichergestellt, welcher überprüft, ob der Absender der Transaktion (
msg.sender
) nicht gleich dem Begünstigten (beneficiary) ist. - Das abgegebene Gebot muss höher sein als das aktuelle Höchstgebot. Dies wird durch die Anweisung
require(msg.value > highestBid);
sichergestellt. - Falls es ein vorheriges Gebot gab (d.h. wenn
highestBid
nicht 0 ist), wird der Betrag des vorherigen Gebots zur Rückgabe für den vorherigen Höchstbietenden markiert. Dies geschieht durch die Anweisung pendingReturns[highestBidder] += highestBid;
. - Schließlich wird der Absender der Transaktion (
msg.sender
) als neuer Höchstbietender (highestBidder
) und der bezahlte Betrag (msg.value
) als neues Höchstgebot (highestBid
) festgelegt.
- Erklärung der Funktionsweise und des Zwecks der withdraw-Funktion: Die withdraw-Funktion ermöglicht es Benutzern, ihre vorherigen Gebote zurückzuziehen und zurückzuerhalten, wenn sie nicht mehr das höchste Gebot halten. Dies geschieht folgendermaßen:
- Es wird der von
msg.sender
(dem Absender der Transaktion) zur Rückgabe markierte Betrag aus der pendingReturns
Mapping abgerufen und in der Variablen amount
gespeichert. - Es wird überprüft, ob der Betrag größer als 0 ist (
require(amount > 0);
), um sicherzustellen, dass der Absender tatsächlich einen rückzuerstattenden Betrag hat. - Der rückzuerstattende Betrag wird zurückgesetzt (
pendingReturns[msg.sender] = 0;
), um doppelte Rückbuchungen zu verhindern. - Der rückzuerstattende Betrag wird auf die Adresse des Absenders (
payable(msg.sender)
) übertragen, indem die Funktion transfer(amount)
aufgerufen wird.
Der Zweck der withdraw-Funktion besteht darin, sicherzustellen, dass Benutzer ihre Gebote zurückziehen können, wenn sie überboten wurden, und so ihr Ether wieder zurückerhalten können.